Load libraries

library(plotly)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio

Attaching package: ‘plotly’

The following object is masked from ‘package:igraph’:

    groups

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout

There were 16 warnings (use warnings() to see them)

Prepare the data

Import

Metadata:

metadata <- read_csv("Metadata.csv")

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  `Sample Name` = col_character(),
  Replicate = col_character(),
  Type = col_character(),
  SizeFraction = col_character(),
  Season = col_character(),
  OxCond = col_character()
)
ℹ Use `spec()` for the full column specifications.

Import SRA table and match SRA IDs with sample IDs in metadata file

SRARunTable <- read_csv("sra_data/SraRunTable.txt")

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_character(),
  AvgSpotLen = col_double(),
  Bases = col_double(),
  Bytes = col_double(),
  ReleaseDate = col_datetime(format = ""),
  Depth_m = col_double(),
  CH4_uM = col_double(),
  H2S_Um = col_double(),
  Oxygen_uM = col_double(),
  Particulate_Sulfur_uM = col_double(),
  salinity = col_double(),
  Temperature_degree_C = col_double(),
  TZVS_uM = col_double()
)
ℹ Use `spec()` for the full column specifications.
metadata <- left_join(metadata, SRARunTable, by = 'Sample Name')

DADA2 results:

# Import Count table. Skip first row of tsv file, which is just some text
count_table <- read_tsv(file="dada2_export/ASVs_counts.tsv")
Missing column names filled in: 'X1' [1]
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  X1 = col_character()
)
ℹ Use `spec()` for the full column specifications.
# And specify that the first column of data are rownames
count_table <- column_to_rownames(count_table, var = colnames(count_table)[1])

# Import taxonomy of ASVs
taxonomy <- read_tsv(file="dada2_export/ASVs_taxonomy.tsv")
Missing column names filled in: 'X1' [1]
── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
cols(
  X1 = col_character(),
  Kingdom = col_character(),
  Supergroup = col_character(),
  Division = col_character(),
  Class = col_character(),
  Order = col_character(),
  Family = col_character(),
  Genus = col_character(),
  Species = col_character()
)
# And specify that the first column of data are rownames
taxonomy <- column_to_rownames(taxonomy, var = colnames(taxonomy)[1])

QC and Filtering

Rarefaction curves

# Use rarecurve, from the Vegan package. Rarcurve expects the dataset as a dataframe so we need to use as.data.frame again:
count_table_df <- as.data.frame(count_table)

# Plot the rarefaction curves, color-coding by the colors listed in sample_info_tab, which indicate sample type, and transforming using t() again
# Running this 5-10 samples at a time because otherwise it takes a long time to render
rarecurve(t(count_table_df), step=100, cex=0.5, ylab="ASVs", label=T)

Remove singletons

count_table_no_singletons <- filter(count_table,rowSums(count_table)>1)
# retains all ASVs (out of 14176)

and change sample names from NCBI ID to our internal sample IDs

# Modify taxa names in count_table_no_singletons, which are the NCBI SRA numbers. Want to use our internal sample key
key <- SRARunTable %>% select(Run, 'Sample Name')

x <- (t(count_table_no_singletons))
x <- as.data.frame(cbind(x, Run = rownames(x)))

y <- t(left_join(x, key, by = "Run"))
colnames(y) <- y['Sample Name',]
y <- y[ !(rownames(y) %in% c('Sample Name', 'Run')), ]

count_table_2 <- type_convert(as.data.frame(y))

── Column specification ───────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double()
)
ℹ Use `spec()` for the full column specifications.

Make Phylo Tree

This process takes a LONG time so run once and save .RData object In the Dada2 tools, there are no options to build a tree (unlike in Qiime2) but we can build it here using DECIPHER and phangorn

(Based on https://f1000research.com/articles/5-1492/v2)

Make an alignment using tools from Decipher (Note- alignment step takes several hours. Commented out for now. Only need to run once)

## import fasta
# fas <- "dada2_export/ASVs.fa"
# seqs <- readDNAStringSet(fas)
# seqs
# 
# # perform the alignment
# aligned <- AlignSeqs(seqs) # automatically detects and uses all cores
# 
# # view the alignment in a browser (optional)
# BrowseSeqs(aligned, highlight=0)
# 
# # write out aligned sequence file
# writeXStringSet(aligned, file="ASVs.aligned.fasta")

Use phangorn package to build tree. Here we are building a maximum likelihood neighbor-joining tree. (Also takes a while to run. Comment out for now.)

# phang.align <- phyDat(as(aligned, "matrix"), type="DNA") # convert to phyDat format
# dm <- dist.ml(phang.align) # calculate pairwise distance matrix
# treeNJ <- NJ(dm) # perform neighbor-joining tree method
# fit = pml(treeNJ, data=phang.align) # compute intermal max likelihood

Save and re-load dataset

Since the step above takes a long time, save all variables up to this point in environment as RData object

save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_tree.RData")

Re-load

load("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_tree.RData")

Make phyloseq objects

Here we will do ordinations using the phyloseq package, which first requires making phyloseq objects out of each of our input data tables (in the last tutorial, I imported the tree using phyloseq so it is already a phyloseq object)

ASV =   otu_table(count_table_2, taxa_are_rows =  TRUE)
There were 15 warnings (use warnings() to see them)
TAX =   tax_table(as.matrix(taxonomy))
META    =   sample_data(data.frame(metadata, row.names = metadata$`Sample Name`))
TREE = phy_tree(fit$tree)

First check that the inputs are in compatible formats by checking for ASV names with the phyloseq function, taxa_names

head(taxa_names(TAX))
[1] "ASV_1" "ASV_2" "ASV_3" "ASV_4" "ASV_5" "ASV_6"
head(taxa_names(ASV))
[1] "ASV_1" "ASV_2" "ASV_3" "ASV_4" "ASV_5" "ASV_6"
head(taxa_names(TREE))
[1] "ASV_1" "ASV_2" "ASV_3" "ASV_4" "ASV_5" "ASV_6"

And check sample names were also detected

head(sample_names(ASV))
[1] "AE3a103A"  "AE3b103A"  "AE1b900AM" "AE3a103B"  "AE3b103B"  "AE3a198B" 
head(sample_names(META))
[1] "AE3a103A" "AE3b103A" "AE3a198A" "AE3b198A" "AE3a234A" "AE3b234A"

And make the phyloseq object

ps <- phyloseq(ASV, TAX,    META , TREE)

Check some features of the phyloseq object

rank_names(ps)
[1] "Kingdom"    "Supergroup" "Division"   "Class"      "Order"      "Family"     "Genus"     
[8] "Species"   
table(tax_table(ps)[, "Supergroup"], exclude = NULL)

     Alveolata      Amoebozoa       Apusozoa Archaeplastida       Excavata       Hacrobia 
          8880              9             45            108              9            395 
  Opisthokonta       Rhizaria  Stramenopiles           <NA> 
           768           2405           1086            471 
unique(tax_table(ps)[, "Supergroup"])
Taxonomy Table:     [10 taxa by 1 taxonomic ranks]:
         Supergroup      
ASV_1    "Alveolata"     
ASV_2    "Rhizaria"      
ASV_6    "Stramenopiles" 
ASV_18   "Opisthokonta"  
ASV_78   "Hacrobia"      
ASV_148  "Archaeplastida"
ASV_193  NA              
ASV_557  "Apusozoa"      
ASV_1114 "Amoebozoa"     
ASV_2665 "Excavata"      

Filter out those ambigious Supergroup annotations- losing 471 ASVs

ps <- subset_taxa(ps, !is.na(Supergroup) & !Supergroup %in% c("", "NA"))
table(tax_table(ps)[, "Supergroup"], exclude = NULL)

     Alveolata      Amoebozoa       Apusozoa Archaeplastida       Excavata       Hacrobia 
          8880              9             45            108              9            395 
  Opisthokonta       Rhizaria  Stramenopiles 
           768           2405           1086 

Check out the Division names

table(tax_table(ps)[, "Division"], exclude = NULL)

        Apicomplexa      Apusomonadidae      Centroheliozoa            Cercozoa 
                 29                  26                  40                 246 
        Chlorophyta    Choanoflagellida          Ciliophora         Cryptophyta 
                 64                  54                 407                  50 
     Dinoflagellata             Discoba        Foraminifera               Fungi 
               8330                   1                   2                  57 
         Haptophyta         Hilomonadea Katablepharidophyta              Lobosa 
                215                  17                   2                   9 
      Mesomycetozoa          Metamonada             Metazoa          Ochrophyta 
                 17                   8                 561                 453 
           Opalozoa      Opisthokonta_X           Perkinsea             Picozoa 
                216                  14                   5                  61 
        Pseudofungi          Radiolaria          Rhodophyta           Sagenista 
                 72                2155                   4                 186 
    Stramenopiles_X        Streptophyta           Telonemia                <NA> 
                 61                  38                  27                 278 

Filter out any with “NA” as Division

ps <- subset_taxa(ps, !is.na(Division) & !Division %in% c(""))
table(tax_table(ps)[, "Division"], exclude = NULL)

        Apicomplexa      Apusomonadidae      Centroheliozoa            Cercozoa 
                 29                  26                  40                 246 
        Chlorophyta    Choanoflagellida          Ciliophora         Cryptophyta 
                 64                  54                 407                  50 
     Dinoflagellata             Discoba        Foraminifera               Fungi 
               8330                   1                   2                  57 
         Haptophyta         Hilomonadea Katablepharidophyta              Lobosa 
                215                  17                   2                   9 
      Mesomycetozoa          Metamonada             Metazoa          Ochrophyta 
                 17                   8                 561                 453 
           Opalozoa      Opisthokonta_X           Perkinsea             Picozoa 
                216                  14                   5                  61 
        Pseudofungi          Radiolaria          Rhodophyta           Sagenista 
                 72                2155                   4                 186 
    Stramenopiles_X        Streptophyta           Telonemia 
                 61                  38                  27 

After the above, 13,427 ASVs remain from the original 14,177

Eliminate the libraries that didn’t have many sequences, AE3a198A, AE3b314A, AE2a200A, AE2b900AN, AE2a200B, AE2a267B, AE2a900BN

taxa_to_keep <- !sample_names(ps) %in% c("AE3a198A","AE3b314A","AE2a200A","AE2b900AN","AE2a200B","AE2a267B","AE2a900BN")
ps <- prune_samples(taxa_to_keep, ps)

41 samples remain and stil 13,427 ASVs

Check rarefaction curve again to make sure those low-sqeuencing-effort samples have been removed

rarecurve(t(otu_table(ps)), step=100, cex=0.5, ylab="ASVs", label=T)

Re-root tree

Have to do this because you may have removed the root of your tree when pruning). (I found this handy function from here which picks the longest branch to root from).

There were 15 warnings (use warnings() to see them)
# first define function from link above to find furthest outgroup
pick_new_outgroup <- function(tree.unrooted){
require("magrittr")
require("data.table")
require("ape") # ape::Ntip
# tablify parts of tree that we need.
treeDT <- 
     cbind(
         data.table(tree.unrooted$edge),
         data.table(length = tree.unrooted$edge.length)
     )[1:Ntip(tree.unrooted)] %>% 
 cbind(data.table(id = tree.unrooted$tip.label))
 # Take the longest terminal branch as outgroup
 new.outgroup <- treeDT[which.max(length)]$id
 return(new.outgroup) }

# then run on my phyloseq tree
my.tree <- phy_tree(ps)
out.group <- pick_new_outgroup(my.tree)
out.group
[1] "ASV_10740"
# Then use this outgroup to root the tree
new.tree1 <- ape::root(my.tree, outgroup=out.group, resolve.root=TRUE)


phy_tree(ps) <- new.tree1

# Check if tree is binary (dichotomous not multichotomous)
is.binary.tree(phy_tree(ps))
[1] TRUE
# If false, would have to run
# new.tree2 <- ape::multi2di(new.tree1)
# phy_tree(ps) <- new.tree2
# phy_tree(ps)

Check phyla using bar plots

Check overall how the phyla are distributed among samples. Phyloseq makes this easy

# First aglomerate the ASVs at the phylum level using the phyloseq function, tax_glom
DivisionGlommed = tax_glom(ps, "Division")

# There are many phyla here, so have to make a custom color palette by interpolating from an existing one in RColorBrewer
colourCount = length(table(tax_table(ps)[, "Division"], exclude = NULL))
getPalette = colorRampPalette(brewer.pal(11, "Spectral"))
DivisionPalette = getPalette(colourCount)

# and plot
plot_bar(DivisionGlommed, x = "Sample", fill = "Division") + 
  scale_fill_manual(values = DivisionPalette)

Plot compositional (relative abundances) instead of absolute abundance using microbiome::transform

ps_ra <- microbiome::transform(ps, transform = "compositional")
(otu_table(ps_ra))[1:5,1:5]
OTU Table:          [5 taxa and 5 samples]
                     taxa are rows
          AE3a103A    AE3b103A    AE1b900AM     AE3a103B     AE3b103B
ASV_1 4.046390e-04 0.000105531 2.462054e-05 0.000000e+00 2.400346e-05
ASV_2 0.000000e+00 0.000000000 3.132963e-02 0.000000e+00 5.600807e-05
ASV_3 6.674871e-03 0.014117702 2.265089e-02 3.696079e-03 1.055352e-02
ASV_4 1.244014e-03 0.001524337 1.231027e-05 4.769134e-05 6.720968e-04
ASV_5 2.675299e-05 0.000000000 0.000000e+00 7.948557e-06 1.040150e-04
# Then aglomerate the ASVs at the phylum level using the phyloseq function, tax_glom
DivisionGlommed_RA = tax_glom(ps_ra, "Division")
# and plot
Division_barplot <- plot_bar(DivisionGlommed_RA, x = "Sample", fill = "Division") + 
  scale_fill_manual(values = DivisionPalette) +
  theme(legend.text = element_text(size = 10))
  

Division_barplot

# export
ggsave("Figures/Division_barplot.eps",Division_barplot, width = 15, height = 5, units = c("in"))

Lots of dinoflagellates and radiolaria. Makes sense. But the above is the distribution from all samples. Next make plots that indicate distributions across environmental gradients. Calculate averages and use bubble plots

Calculate averages from replicates

Get average relative abundances from sample replicates


otu_table_mean_ra <- 
  mutate(data.frame(otu_table(ps_ra)), "103A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a103A","AE3b103A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "198A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3b198A")), na.rm = TRUE))  %>% # Sample AE3a198A was removed
  mutate(data.frame(otu_table(ps_ra)), "234A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a234A","AE3b234A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "295A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a295A","AE3b295A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "314A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a314A")), na.rm = TRUE)) %>%  # Sample AE3b314A was removed
  mutate(data.frame(otu_table(ps_ra)), "900AM" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a900AM","AE1b900AM")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "103B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a103B","AE3b103B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "198B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a198B","AE3b198B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "234B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a234B","AE3b234B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "295B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a295B","AE3b295B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "314B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a314B","AE3b314B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "900BM" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE3a900BM","AE1b900BM")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "143A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a143A","AE2b143A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "200A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b200A")), na.rm = TRUE)) %>% # AE2a200A was removed
  mutate(data.frame(otu_table(ps_ra)), "237A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a237A","AE2b237A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "247A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a247A","AE2b247A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "267A" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a267A","AE2b267A")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "900AN" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a900AN")), na.rm = TRUE)) %>% # AE2b900AN was removed
  mutate(data.frame(otu_table(ps_ra)), "143B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a143B","AE2b143B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "200B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b200B")), na.rm = TRUE)) %>% # AE2a200B was removed
  mutate(data.frame(otu_table(ps_ra)), "237B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a237B","AE2b237B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "247B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2a247B","AE2b247B")), na.rm = TRUE)) %>%
  mutate(data.frame(otu_table(ps_ra)), "267B" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b267B")), na.rm = TRUE)) %>% # AE2a267B was removed
  mutate(data.frame(otu_table(ps_ra)), "900BN" = rowMeans(select(data.frame(otu_table(ps_ra)), c("AE2b900BN")), na.rm = TRUE)) # AE2a900BN was removed

otu_table_mean_ra <- otu_table_mean_ra[,unique(metadata$Replicate)]

otu_table_mean_ra

Make into new phyloseq object

metadata2 <- unique(select(metadata,!c('Sample Name',Type,colnames(SRARunTable))))
META2 <- sample_data(data.frame(metadata2, row.names = metadata2$Replicate))

ps_ra_mean <- phyloseq(otu_table(otu_table_mean_ra, taxa_are_rows = TRUE), TAX, TREE, META2)
# First aglomerate the ASVs at the phylum level using the phyloseq function, tax_glom
ps_ra_mean_division <- tax_glom(ps_ra_mean, "Division")


# and check by bar plotting
plot_bar(ps_ra_mean_division, x = "Sample", fill = "Division") + 
  scale_fill_manual(values = DivisionPalette)

Abundance Plots

Prepare data

Extract mean relative abundance, glommed by division, from the phyloseq object and pair it to taxonomic data

division_df <- data.frame(otu_table(ps_ra_mean_division))
colnames(division_df) <- colnames(otu_table(ps_ra_mean_division))
division_df$ASV <- rownames(division_df)

otu_table_mean_ra <- left_join(division_df, as_tibble(taxonomy, rownames = "ASV"), by = "ASV")
otu_table_mean_ra

Pivot longer

otu_table_mean_ra <- pivot_longer(otu_table_mean_ra, cols = unique(metadata$Replicate), names_to = "Replicate", values_to = "Mean_RA")
otu_table_mean_ra

Join metadata

otu_table_mean_ra <- left_join(otu_table_mean_ra, unique(select(metadata, c("Replicate", "Depth", "SizeFraction", "Season", "OxCond", "Fluorescence", "BeamAtt", "O2", "Temp", "Salinity", "H2S", "ParticulateS", "TZVS", "CH4", "NO3", "NO2", "NH4", "PO4", "Chemoautotrophy", "BNP", "MicroAbun(x10^8 L^-1)", "FlagAbun(x10^5 L-1)", "VLP(x10^8 L-1)"))), by = "Replicate")

# Replace zeroes in RA with NA (better for plotting)
otu_table_mean_ra$Mean_RA[otu_table_mean_ra$Mean_RA == 0] <- NA

otu_table_mean_ra

Bubble Plot of Divisions

# reorder some factors to make them plot in the order I want
otu_table_mean_ra$OxCond <- factor(otu_table_mean_ra$OxCond, levels = c("Oxycline", "ShallowAnoxic", "Euxinic"))
otu_table_mean_ra$SizeFraction <- factor(otu_table_mean_ra$SizeFraction, levels = c("PA", "FL"))

euk_divisions_bubbleplot_color <- ggplot(otu_table_mean_ra,aes (x = as.character(Depth), y = reorder(Division, Mean_RA, function(x){sum(x,na.rm = TRUE)}), color = OxCond)) + 
  geom_point(aes(size =Mean_RA))+
  facet_wrap(Season~SizeFraction, scales = "free_x", drop= TRUE, ncol = 4) +
  scale_size(range = c(1,15)) +
  scale_size_area(breaks = c(0,.25,.5,.75,1), max_size = 6) +
  xlab("Depth") +
  ylab("") +
  labs(size="Relative Abundance", color = "Redox Condition") +
   scale_color_manual(values = c("blue", "red", "brown4")) +
  theme_bw() +
  theme(axis.text.x = element_text(size=10),
        axis.text.y = element_text(size=10),
        axis.title.x= element_text(size=12),
        axis.title.y= element_text(size=12))
Scale for 'size' is already present. Adding another scale for 'size', which will replace
the existing scale.
euk_divisions_bubbleplot_color

Save figure

ggsave(filename = "Figures/euk_divisions_bubbleplot_color.eps", plot = euk_divisions_bubbleplot_color, units = c("in"), width = 10, height = 6, dpi = 300)

Save and re-load dataset

save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_bubbleplots.RData")

Re-load

load("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_bubbleplots.RData")

Correlation Analyses

Prepare the data

Import prokaryote dataset from Suter et al. 2018

Import

NA
NA

Get sample names

bac_samples <- colnames(bac_counts)[2:49]
arch_samples <- colnames(arch_counts)[2:47]

bac_samples
 [1] "AB2a237B"  "AB2b267B"  "AB2a267B"  "AB2b237B"  "AB3b103B"  "AB2b200A"  "AB2b247B" 
 [8] "AB3a103B"  "AB3b314B"  "AB2a247B"  "AB3b295B"  "AB2a200B"  "AB3a295B"  "AB3a314B" 
[15] "AB2a237A"  "AB3b198B"  "AB2b143B"  "AB2a143B"  "AB3a198A"  "AB3b234B"  "AB3a198B" 
[22] "AB2a247A"  "AB2b200B"  "AB3b198A"  "AB3b234A"  "AB3a234B"  "AB3a295A"  "AB3a314A" 
[29] "AB2a900BN" "AB3a900B"  "AB2b900BN" "AB2b267A"  "AB3b295A"  "AB2a900AN" "AB2b143A" 
[36] "AB3b103A"  "AB3a103A"  "AB3a234A"  "AB2b247A"  "AB3b314A"  "AB2b900AN" "AB2a267A" 
[43] "AB1b900A"  "AB2b237A"  "AB2a143A"  "AB1b900B"  "AB2a200A"  "AB3a900A" 
arch_samples
 [1] "AA3a314A"  "AA2a237B"  "AA2b237A"  "AA3a900B"  "AA1b900A"  "AA3a103B"  "AA2a247A" 
 [8] "AA1b900B"  "AA3b198A"  "AA2b237B"  "AA2b143A"  "AA3b103A"  "AA2a143A"  "AA3a103A" 
[15] "AA3a198A"  "AA2a200A"  "AA3b198B"  "AA3b234B"  "AA2a200B"  "AA2a143B"  "AA3a234A" 
[22] "AA3a295B"  "AA2b247B"  "AA3a234B"  "AA2b247A"  "AA3b234A"  "AA3b314A"  "AA3a314B" 
[29] "AA3a198B"  "AA3b295A"  "AA3a295A"  "AA2a237A"  "AA3a900A"  "AA2b200A"  "AA2b267B" 
[36] "AA3b314B"  "AA2b143B"  "AA2b200B"  "AA3b103B"  "AA3b295B"  "AA2a267B"  "AA2a247B" 
[43] "AA2a900AN" "AA2b900BN" "AA2a900BN" "AA2b900AN"

Make separate taxonomy and count variables

arch_OTU <- arch_counts[,c("#OTU ID",arch_samples)]
arch_taxonomy <-  arch_counts %>%
  select(-arch_samples)  %>%
  select(-Sum)

arch_OTU
arch_taxonomy

bac_OTU <- bac_counts[,c("#OTU ID",bac_samples)]
bac_taxonomy <-  bac_counts %>%
  select(-bac_samples)  %>%
  select(-Sum) %>%
  select(-"Interesting close relatives")

bac_OTU
bac_taxonomy

Make into phyloseq objects

bac_OTU <- type_convert(as.data.frame(bac_OTU))

── Column specification ──────────────────────────────────────────────────────────────────────
cols(
  `#OTU ID` = col_character()
)
rownames(bac_OTU) <- bac_OTU$`#OTU ID`
bac_OTU <- bac_OTU[,!names(bac_OTU) %in% (c("#OTU ID"))]

bac_OTU =   otu_table(bac_OTU, taxa_are_rows =  TRUE)
#
arch_OTU <- type_convert(as.data.frame(arch_OTU))

── Column specification ──────────────────────────────────────────────────────────────────────
cols(
  `#OTU ID` = col_character()
)
rownames(arch_OTU) <- arch_OTU$`#OTU ID`
arch_OTU <- arch_OTU[,!names(arch_OTU) %in% (c("#OTU ID"))]

arch_OTU    =   otu_table(arch_OTU, taxa_are_rows =  TRUE)
#
bac_TAX <- type_convert(as.data.frame(bac_taxonomy))

── Column specification ──────────────────────────────────────────────────────────────────────
cols(
  `#OTU ID` = col_character(),
  `Refined taxonomy` = col_character(),
  `taxonomy-1` = col_character(),
  `taxonomy-2` = col_character(),
  `taxonomy-3` = col_character(),
  `taxonomy-4` = col_character(),
  `taxonomy-5` = col_character(),
  `taxonomy-6` = col_character(),
  `taxonomy-7` = col_character(),
  `taxonomy-8` = col_character()
)
rownames(bac_TAX) <- bac_TAX$`#OTU ID`
bac_TAX <- bac_TAX[,!names(bac_TAX) %in% (c("#OTU ID"))]

bac_TAX =   tax_table(as.matrix(bac_TAX))
#
arch_TAX <- type_convert(as.data.frame(arch_taxonomy))

── Column specification ──────────────────────────────────────────────────────────────────────
cols(
  `#OTU ID` = col_character(),
  `taxonomy-1` = col_character(),
  `taxonomy-2` = col_character(),
  `taxonomy-3` = col_character(),
  `taxonomy-4` = col_character(),
  `taxonomy-5` = col_character(),
  `taxonomy-6` = col_character(),
  `taxonomy-7` = col_character()
)
rownames(arch_TAX) <- arch_TAX$`#OTU ID`
arch_TAX <- arch_TAX[,!names(arch_TAX) %in% (c("#OTU ID"))]

arch_TAX    =   tax_table(as.matrix(arch_TAX))
#
META    =   sample_data(data.frame(metadata, row.names = metadata$`Sample Name`))
#

ps_bac <- phyloseq(bac_OTU, bac_TAX,    META)
ps_arch <- phyloseq(arch_OTU,   arch_TAX,   META)

Filter out the samples with low sequencing effort. These were previously identified for itags paper

taxa_to_keep_b <- !sample_names(ps_bac) %in% c("AB3a900A","AB2a200A","AB2b267A")
ps_bac <- prune_samples(taxa_to_keep_b, ps_bac)

taxa_to_keep_a <- !sample_names(ps_arch) %in% c("AA2b900AN","AA2a247B","AA2a900BN","AA2b900BN")
ps_arch <- prune_samples(taxa_to_keep_a, ps_arch)

Filtering

First calculate relative abdunance of bac and arch OTU tables

ps_bac_ra <- microbiome::transform(ps_bac, transform = "compositional")
(otu_table(ps_bac_ra))[1:5,1:5]
OTU Table:          [5 taxa and 5 samples]
                     taxa are rows
                 AB2a237B     AB2b267B     AB2a267B     AB2b237B     AB3b103B
denovo231149 0.2125960670 0.5369359318 0.3471806593 0.2100273349 7.856409e-05
denovo348086 0.0014568676 0.0138326887 0.0023327600 0.0012899324 6.043392e-06
denovo302903 0.0001624382 0.0034240801 0.0009700197 0.0001292371 3.021696e-06
denovo104772 0.0310612290 0.0005181989 0.0143657171 0.0281322315 2.243911e-02
denovo309274 0.0620260104 0.0440345135 0.0583425623 0.0681006294 1.869221e-02
ps_arch_ra <- microbiome::transform(ps_arch, transform = "compositional")
(otu_table(ps_arch_ra))[1:5,1:5]
OTU Table:          [5 taxa and 5 samples]
                     taxa are rows
                AA3a314A   AA2a237B   AA2b237A     AA3a900B    AA1b900A
denovo180502 0.181534684 0.08790871 0.21422859 0.0028892496 0.032998864
denovo80843  0.129102961 0.07914636 0.05450465 0.0043043922 0.029778944
denovo217943 0.025004870 0.21867787 0.16709842 0.0009620487 0.021373974
denovo94410  0.009662359 0.13278112 0.10651172 0.0007448119 0.008834576
denovo199225 0.019405384 0.02748757 0.10588944 0.0008813608 0.009570436

Filter low abundance species from full dataset

Remove rows of glommed taxa from the full dataframe if their sum across all samples doesn’t exceed 5% (RA > 0.05)

# Bacteria
x <- taxa_sums(ps_bac_ra)
# keepTaxa <-  base::which(x  > .05)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_pruned <-  prune_taxa(keepTaxa, ps_bac_ra)
ps_bac_pruned <-  prune_taxa(keepTaxa, ps_bac)
ps_bac_ra_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 124 taxa and 45 samples ]
sample_data() Sample Data:       [ 45 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 124 taxa by 10 taxonomic ranks ]
ps_bac_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 124 taxa and 45 samples ]
sample_data() Sample Data:       [ 45 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 124 taxa by 10 taxonomic ranks ]
# Archaea
x <- taxa_sums(ps_arch_ra)
# keepTaxa <-  base::which(x  > .05)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_pruned <-  prune_taxa(keepTaxa, ps_arch_ra)
ps_arch_pruned <-  prune_taxa(keepTaxa, ps_arch)
ps_arch_ra_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 52 taxa and 42 samples ]
sample_data() Sample Data:       [ 42 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 52 taxa by 8 taxonomic ranks ]
ps_arch_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 52 taxa and 42 samples ]
sample_data() Sample Data:       [ 42 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 52 taxa by 8 taxonomic ranks ]
# Eukaryotes
x <- taxa_sums(ps_ra)
# keepTaxa <-  base::which(x  > .05)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_pruned <-  prune_taxa(keepTaxa, ps_ra)
ps_euk_pruned <-  prune_taxa(keepTaxa, ps)
ps_euk_ra_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 123 taxa and 41 samples ]
sample_data() Sample Data:       [ 41 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 123 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 123 tips and 122 internal nodes ]
ps_euk_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 123 taxa and 41 samples ]
sample_data() Sample Data:       [ 41 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 123 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 123 tips and 122 internal nodes ]

Trimmed to 124 bacteria OTUs, 52 archaea OTUs, and 123 eukaryotic ASVs (299 total). Proceed with this dataset of the most abundant OTUs for correlations and network analyses…

To do the multi-domain analysis, the sample names from each phyloseq object must match. These currently have “B” for bacteria, A, E etc. Remove this letter from sample names so that “AE2a247B”, “AA2a247B”, “AB2a247B” all become just “Type” from the metadata sheet [IntNov1FL in this case- for Interface, November, rep 1, free-living].

Import my SampleKey

samplekey <- read_csv("SampleKey.csv")

── Column specification ──────────────────────────────────────────────────────────────────────
cols(
  Type = col_character(),
  SampleID_bac = col_character(),
  SampleID_arch = col_character(),
  SampleID_euk = col_character()
)

Change the sample names in the otu tables to sample “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_pruned))))
# replace col names of otu table from ps_arch_ra_pruned
sample_names(ps_arch_ra_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_pruned))))
sample_names(ps_bac_ra_pruned) <- samplekey_B$Type
sample_names(ps_bac_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_pruned))))
sample_names(ps_euk_ra_pruned) <- samplekey_E$Type
sample_names(ps_euk_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC

alldomains_df <- bind_rows(data.frame(otu_table(ps_bac_pruned)), data.frame(otu_table(ps_arch_pruned)), data.frame(otu_table(ps_euk_pruned)))
alldomains_df

Change row names from “denovoXXX” to meaningful names

alldomains_df_full <- cbind(ID = rownames(alldomains_df), alldomains_df)

# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full[1:dim(otu_table(ps_bac_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full[sum(dim(otu_table(ps_bac_pruned))[1],1):sum(dim(otu_table(ps_bac_pruned))[1],dim(otu_table(ps_arch_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full[sum(dim(otu_table(ps_arch_pruned))[1], dim(otu_table(ps_bac_pruned))[1],1):sum(dim(otu_table(ps_arch_pruned))[1], dim(otu_table(ps_bac_pruned))[1],dim(otu_table(ps_euk_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full <- rbind(temp1, temp2, temp3)
alldomains_df_full <- data.frame(alldomains_df_full)
rownames(alldomains_df_full) <- alldomains_df_full$New_ID
alldomains_df_full <- select(alldomains_df_full, -c("ID","New_ID"))

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full <- alldomains_df_full %>%
    select_if(~ !any(is.na(.)))
t(alldomains_df_full)[1:5,1:5]
              denovo231149 Proteobacteria Gammaproteobacteria Chromatiales
SubOxNov1FL                                                          41881
AnoxNov2FL                                                          216557
SubOxNov2FL                                                          86132
OxicMay2FL                                                              26
MicroOxNov2PA                                                         7356
              denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)
SubOxNov1FL                                                                           287
AnoxNov2FL                                                                           5579
SubOxNov2FL                                                                           529
OxicMay2FL                                                                              2
MicroOxNov2PA                                                                         123
              denovo302903 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)
SubOxNov1FL                                                                            32
AnoxNov2FL                                                                           1381
SubOxNov2FL                                                                            53
OxicMay2FL                                                                              1
MicroOxNov2PA                                                                          37
              denovo104772 Proteobacteria Alphaproteobacteria SAR11_clade
SubOxNov1FL                                                          6119
AnoxNov2FL                                                            209
SubOxNov2FL                                                         11537
OxicMay2FL                                                           7426
MicroOxNov2PA                                                       20010
              denovo309274 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)
SubOxNov1FL                                                                         12219
AnoxNov2FL                                                                          17760
SubOxNov2FL                                                                         27928
OxicMay2FL                                                                           6186
MicroOxNov2PA                                                                        1362
alldomains_df <- alldomains_df %>%
    select_if(~ !any(is.na(.)))
t(alldomains_df)[1:5,1:5]
              denovo231149 denovo348086 denovo302903 denovo104772 denovo309274
SubOxNov1FL          41881          287           32         6119        12219
AnoxNov2FL          216557         5579         1381          209        17760
SubOxNov2FL          86132          529           53        11537        27928
OxicMay2FL              26            2            1         7426         6186
MicroOxNov2PA         7356          123           37        20010         1362

36 samples remain for correlation

Filter low abundance species- oxycline depths only

Simlarly, make pruned datasets of the most abundant OTUs/ASVs in the oxycline, anoxic, and euxinic samples as separate datasets

Pull out samples and taxa from each redox regime

# Pull out oxycline bacteria sample IDs
oxyclinetypes_bac <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_bac)) %>%
  filter(OxCond == "Oxycline") %>% 
  select("Sample Name")
oxyclinetypes_bac <- unlist(c(unique(oxyclinetypes_bac)), use.names = FALSE)

# Pull out all bacteria from oxycline
ps_bac_oxycline <-  prune_samples(oxyclinetypes_bac, ps_bac)
ps_bac_ra_oxycline <-  prune_samples(oxyclinetypes_bac, ps_bac_ra)


# Pull out oxycline archaea sample IDs
oxyclinetypes_arch <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_arch)) %>%
  filter(OxCond == "Oxycline") %>% 
  select("Sample Name")
oxyclinetypes_arch <- unlist(c(unique(oxyclinetypes_arch)), use.names = FALSE)

# Pull out all archaea from oxycline
ps_arch_oxycline <-  prune_samples(oxyclinetypes_arch, ps_arch)
ps_arch_ra_oxycline <-  prune_samples(oxyclinetypes_arch, ps_arch_ra)


# Pull out oxycline eukaryotic sample IDs
oxyclinetypes_euk <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps)) %>%
  filter(OxCond == "Oxycline") %>% 
  select("Sample Name")
oxyclinetypes_euk <- unlist(c(unique(oxyclinetypes_euk)), use.names = FALSE)

# Pull out all eukaryotes from oxycline
ps_euk_oxycline <-  prune_samples(oxyclinetypes_euk, ps)
ps_euk_ra_oxycline <-  prune_samples(oxyclinetypes_euk, ps_ra)

Filter out low abundance taxa from the oxycline samples. Use 5% as cutoff

# Bacteria
x <- taxa_sums(ps_bac_ra_oxycline)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_oxycline_pruned <-  prune_taxa(keepTaxa, ps_bac_ra_oxycline)
ps_bac_oxycline_pruned <-  prune_taxa(keepTaxa, ps_bac_oxycline)
ps_bac_ra_oxycline_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 79 taxa and 23 samples ]
sample_data() Sample Data:       [ 23 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 79 taxa by 10 taxonomic ranks ]
ps_bac_oxycline_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 79 taxa and 23 samples ]
sample_data() Sample Data:       [ 23 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 79 taxa by 10 taxonomic ranks ]
# Archaea
x <- taxa_sums(ps_arch_ra_oxycline)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_oxycline_pruned <-  prune_taxa(keepTaxa, ps_arch_ra_oxycline)
ps_arch_oxycline_pruned <-  prune_taxa(keepTaxa, ps_arch_oxycline)
ps_arch_ra_oxycline_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 36 taxa and 24 samples ]
sample_data() Sample Data:       [ 24 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 36 taxa by 8 taxonomic ranks ]
ps_arch_oxycline_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 36 taxa and 24 samples ]
sample_data() Sample Data:       [ 24 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 36 taxa by 8 taxonomic ranks ]
# Eukaryotes
x <- taxa_sums(ps_euk_ra_oxycline)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_oxycline_pruned <-  prune_taxa(keepTaxa, ps_euk_ra_oxycline)
ps_euk_oxycline_pruned <-  prune_taxa(keepTaxa, ps_euk_oxycline)
ps_euk_ra_oxycline_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 76 taxa and 21 samples ]
sample_data() Sample Data:       [ 21 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 76 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 76 tips and 75 internal nodes ]
ps_euk_oxycline_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 76 taxa and 21 samples ]
sample_data() Sample Data:       [ 21 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 76 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 76 tips and 75 internal nodes ]

79 bacteria, 36 archaea, 76 eukaryota remain

Change the sample names in the otu tables to “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_oxycline_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_oxycline_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_oxycline_pruned))))
# replace col names of otu table from ps_arch_ra_oxycline_pruned
sample_names(ps_arch_ra_oxycline_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_oxycline_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_oxycline_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_oxycline_pruned))))
sample_names(ps_bac_ra_oxycline_pruned) <- samplekey_B$Type
sample_names(ps_bac_oxycline_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_oxycline_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_oxycline_pruned))))
sample_names(ps_euk_ra_oxycline_pruned) <- samplekey_E$Type
sample_names(ps_euk_oxycline_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC

alldomains_df_oxycline <- bind_rows(data.frame(otu_table(ps_bac_oxycline_pruned)), data.frame(otu_table(ps_arch_oxycline_pruned)), data.frame(otu_table(ps_euk_oxycline_pruned)))
alldomains_df_oxycline

Change row names from “denovoXXX” to meaningful names

alldomains_df_full_oxycline <- cbind(ID = rownames(alldomains_df_oxycline), alldomains_df_oxycline)

# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full_oxycline[1:dim(otu_table(ps_bac_oxycline_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full_oxycline[sum(dim(otu_table(ps_bac_oxycline_pruned))[1],1):sum(dim(otu_table(ps_bac_oxycline_pruned))[1],dim(otu_table(ps_arch_oxycline_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full_oxycline[sum(dim(otu_table(ps_arch_oxycline_pruned))[1], dim(otu_table(ps_bac_oxycline_pruned))[1],1):sum(dim(otu_table(ps_arch_oxycline_pruned))[1], dim(otu_table(ps_bac_oxycline_pruned))[1],dim(otu_table(ps_euk_oxycline_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full_oxycline <- rbind(temp1, temp2, temp3)
alldomains_df_full_oxycline <- data.frame(alldomains_df_full_oxycline)
rownames(alldomains_df_full_oxycline) <- alldomains_df_full_oxycline$New_ID
alldomains_df_full_oxycline <- select(alldomains_df_full_oxycline, -c("ID","New_ID"))
alldomains_df_full_oxycline

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full_oxycline <- alldomains_df_full_oxycline %>%
    select_if(~ !any(is.na(.)))
alldomains_df_full_oxycline

alldomains_df_oxycline <- alldomains_df_oxycline %>%
    select_if(~ !any(is.na(.)))
alldomains_df_oxycline

21 samples remain for correlation

Filter low abundance species- anoxic depths only

Pull out samples from shallow anoxic regime

# Pull out anoxic layer bacteria sample IDs
anoxictypes_bac <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_bac)) %>%
  filter(OxCond == "ShallowAnoxic") %>% 
  select("Sample Name")
anoxictypes_bac <- unlist(c(unique(anoxictypes_bac)), use.names = FALSE)

# Pull out all bacteria from anoxic layer
ps_bac_anoxic <-  prune_samples(anoxictypes_bac, ps_bac)
ps_bac_ra_anoxic <-  prune_samples(anoxictypes_bac, ps_bac_ra)


# Pull out anoxic layer archaea sample IDs
anoxictypes_arch <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_arch)) %>%
  filter(OxCond == "ShallowAnoxic") %>% 
  select("Sample Name")
anoxictypes_arch <- unlist(c(unique(anoxictypes_arch)), use.names = FALSE)

# Pull out all archaea from anoxic layer
ps_arch_anoxic<-  prune_samples(anoxictypes_arch, ps_arch)
ps_arch_ra_anoxic <-  prune_samples(anoxictypes_arch, ps_arch_ra)


# Pull out anoxic layer eukaryotic sample IDs
anoxictypes_euk <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps)) %>%
  filter(OxCond == "ShallowAnoxic") %>% 
  select("Sample Name")
anoxictypes_euk <- unlist(c(unique(anoxictypes_euk)), use.names = FALSE)

# Pull out all eukaryotes from anoxic layer
ps_euk_anoxic <-  prune_samples(anoxictypes_euk, ps)
ps_euk_ra_anoxic <-  prune_samples(anoxictypes_euk, ps_ra)

Filter out low abundance taxa from the oxycline samples. Use 5% as cutoff

# Bacteria
x <- taxa_sums(ps_bac_ra_anoxic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_anoxic_pruned <-  prune_taxa(keepTaxa, ps_bac_ra_anoxic)
ps_bac_anoxic_pruned <-  prune_taxa(keepTaxa, ps_bac_anoxic)
ps_bac_ra_anoxic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 32 taxa and 15 samples ]
sample_data() Sample Data:       [ 15 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 32 taxa by 10 taxonomic ranks ]
ps_bac_anoxic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 32 taxa and 15 samples ]
sample_data() Sample Data:       [ 15 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 32 taxa by 10 taxonomic ranks ]
# Archaea
x <- taxa_sums(ps_arch_ra_anoxic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_anoxic_pruned <-  prune_taxa(keepTaxa, ps_arch_ra_anoxic)
ps_arch_anoxic_pruned <-  prune_taxa(keepTaxa, ps_arch_anoxic)
ps_arch_ra_anoxic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 19 taxa and 13 samples ]
sample_data() Sample Data:       [ 13 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 19 taxa by 8 taxonomic ranks ]
ps_arch_anoxic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 19 taxa and 13 samples ]
sample_data() Sample Data:       [ 13 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 19 taxa by 8 taxonomic ranks ]
# Eukaryotes
x <- taxa_sums(ps_euk_ra_anoxic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_anoxic_pruned <-  prune_taxa(keepTaxa, ps_euk_ra_anoxic)
ps_euk_anoxic_pruned <-  prune_taxa(keepTaxa, ps_euk_anoxic)
ps_euk_ra_anoxic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 37 taxa and 14 samples ]
sample_data() Sample Data:       [ 14 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 37 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 37 tips and 36 internal nodes ]
ps_euk_anoxic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 37 taxa and 14 samples ]
sample_data() Sample Data:       [ 14 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 37 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 37 tips and 36 internal nodes ]

32 bacteria, 19 archaea, 37 eukaryota remain

Change the sample names in the otu tables to “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_anoxic_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_anoxic_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_anoxic_pruned))))
# replace col names of otu table from ps_arch_ra_anoxic_pruned
sample_names(ps_arch_ra_anoxic_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_anoxic_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_anoxic_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_anoxic_pruned))))
sample_names(ps_bac_ra_anoxic_pruned) <- samplekey_B$Type
sample_names(ps_bac_anoxic_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_anoxic_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_anoxic_pruned))))
sample_names(ps_euk_ra_anoxic_pruned) <- samplekey_E$Type
sample_names(ps_euk_anoxic_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC

alldomains_df_anoxic <- bind_rows(data.frame(otu_table(ps_bac_anoxic_pruned)), data.frame(otu_table(ps_arch_anoxic_pruned)), data.frame(otu_table(ps_euk_anoxic_pruned)))
alldomains_df_anoxic

Change row names from “denovoXXX” to meaningful names

alldomains_df_full_anoxic <- cbind(ID = rownames(alldomains_df_anoxic), alldomains_df_anoxic)

# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full_anoxic[1:dim(otu_table(ps_bac_anoxic_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full_anoxic[sum(dim(otu_table(ps_bac_anoxic_pruned))[1],1):sum(dim(otu_table(ps_bac_anoxic_pruned))[1],dim(otu_table(ps_arch_anoxic_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full_anoxic[sum(dim(otu_table(ps_arch_anoxic_pruned))[1], dim(otu_table(ps_bac_anoxic_pruned))[1],1):sum(dim(otu_table(ps_arch_anoxic_pruned))[1], dim(otu_table(ps_bac_anoxic_pruned))[1],dim(otu_table(ps_euk_anoxic_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full_anoxic <- rbind(temp1, temp2, temp3)
alldomains_df_full_anoxic <- data.frame(alldomains_df_full_anoxic)
rownames(alldomains_df_full_anoxic) <- alldomains_df_full_anoxic$New_ID
alldomains_df_full_anoxic <- select(alldomains_df_full_anoxic, -c("ID","New_ID"))
alldomains_df_full_anoxic

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full_anoxic <- alldomains_df_full_anoxic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_full_anoxic

alldomains_df_anoxic <- alldomains_df_anoxic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_anoxic

11 samples remain for correlation

Filter low abundance species- euxinic depths only

Pull out samples from shallow anoxic regime

# Pull out anoxic layer bacteria sample IDs
euxinictypes_bac <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_bac)) %>%
  filter(OxCond == "Euxinic") %>% 
  select("Sample Name")
euxinictypes_bac <- unlist(c(unique(euxinictypes_bac)), use.names = FALSE)

# Pull out all bacteria from euxinic layer
ps_bac_euxinic <-  prune_samples(euxinictypes_bac, ps_bac)
ps_bac_ra_euxinic <-  prune_samples(euxinictypes_bac, ps_bac_ra)


# Pull out euxinic layer archaea sample IDs
euxinictypes_arch <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps_arch)) %>%
  filter(OxCond == "Euxinic") %>% 
  select("Sample Name")
euxinictypes_arch <- unlist(c(unique(euxinictypes_arch)), use.names = FALSE)

# Pull out all archaea from euxinic layer
ps_arch_euxinic<-  prune_samples(euxinictypes_arch, ps_arch)
ps_arch_ra_euxinic <-  prune_samples(euxinictypes_arch, ps_arch_ra)


# Pull out euxinic layer eukaryotic sample IDs
euxinictypes_euk <- metadata %>% 
  filter(`Sample Name` %in% sample_names(ps)) %>%
  filter(OxCond == "Euxinic") %>% 
  select("Sample Name")
euxinictypes_euk <- unlist(c(unique(euxinictypes_euk)), use.names = FALSE)

# Pull out all eukaryotes from euxinic layer
ps_euk_euxinic <-  prune_samples(euxinictypes_euk, ps)
ps_euk_ra_euxinic <-  prune_samples(euxinictypes_euk, ps_ra)

Filter out low abundance taxa from the oxycline samples. Use 5% as cutoff

# Bacteria
x <- taxa_sums(ps_bac_ra_euxinic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_bac_ra_euxinic_pruned <-  prune_taxa(keepTaxa, ps_bac_ra_euxinic)
ps_bac_euxinic_pruned <-  prune_taxa(keepTaxa, ps_bac_euxinic)
ps_bac_ra_euxinic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 16 taxa and 7 samples ]
sample_data() Sample Data:       [ 7 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 16 taxa by 10 taxonomic ranks ]
ps_bac_euxinic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 16 taxa and 7 samples ]
sample_data() Sample Data:       [ 7 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 16 taxa by 10 taxonomic ranks ]
# Archaea
x <- taxa_sums(ps_arch_ra_euxinic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_arch_ra_euxinic_pruned <-  prune_taxa(keepTaxa, ps_arch_ra_euxinic)
ps_arch_euxinic_pruned <-  prune_taxa(keepTaxa, ps_arch_euxinic)
ps_arch_ra_euxinic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 16 taxa and 5 samples ]
sample_data() Sample Data:       [ 5 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 16 taxa by 8 taxonomic ranks ]
ps_arch_euxinic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 16 taxa and 5 samples ]
sample_data() Sample Data:       [ 5 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 16 taxa by 8 taxonomic ranks ]
# Eukaryotes
x <- taxa_sums(ps_euk_ra_euxinic)
keepTaxa <-  x>.05 # prune_taxa require a logical not a list of IDs. compare to keepTaxa above to check
ps_euk_ra_euxinic_pruned <-  prune_taxa(keepTaxa, ps_euk_ra_euxinic)
ps_euk_euxinic_pruned <-  prune_taxa(keepTaxa, ps_euk_euxinic)
ps_euk_ra_euxinic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 20 taxa and 6 samples ]
sample_data() Sample Data:       [ 6 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 20 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 20 tips and 19 internal nodes ]
ps_euk_euxinic_pruned
phyloseq-class experiment-level object
otu_table()   OTU Table:         [ 20 taxa and 6 samples ]
sample_data() Sample Data:       [ 6 samples by 66 sample variables ]
tax_table()   Taxonomy Table:    [ 20 taxa by 8 taxonomic ranks ]
phy_tree()    Phylogenetic Tree: [ 20 tips and 19 internal nodes ]

16 bacteria, 16 archaea, 20 eukaryota remain

Change the sample names in the otu tables to “Type”

# Archaea
# remove missing archaea samples from samplekey_A
samplekey_A <- filter(samplekey, SampleID_arch  %in% colnames(otu_table(ps_arch_ra_euxinic_pruned)))
# sort SampleKey by order of column names from ps_arch_ra_euxinic_pruned
samplekey_A <- samplekey_A %>% arrange(factor(SampleID_arch, levels = colnames(otu_table(ps_arch_ra_euxinic_pruned))))
# replace col names of otu table from ps_arch_ra_euxinic_pruned
sample_names(ps_arch_ra_euxinic_pruned) <- samplekey_A$Type
# and ps_arch_pruned
sample_names(ps_arch_euxinic_pruned) <- samplekey_A$Type


# Bacteria
samplekey_B <- filter(samplekey, SampleID_bac  %in% colnames(otu_table(ps_bac_ra_euxinic_pruned)))
samplekey_B <- samplekey_B %>% arrange(factor(SampleID_bac, levels = colnames(otu_table(ps_bac_ra_euxinic_pruned))))
sample_names(ps_bac_ra_euxinic_pruned) <- samplekey_B$Type
sample_names(ps_bac_euxinic_pruned) <- samplekey_B$Type


# Eukaryotes
samplekey_E <- filter(samplekey, SampleID_euk  %in% colnames(otu_table(ps_euk_ra_euxinic_pruned)))
samplekey_E <- samplekey_E %>% arrange(factor(SampleID_euk, levels = colnames(otu_table(ps_euk_ra_euxinic_pruned))))
sample_names(ps_euk_ra_euxinic_pruned) <- samplekey_E$Type
sample_names(ps_euk_euxinic_pruned) <- samplekey_E$Type

Move all pruned otu tables into one table by matching the sample Type- will use this for SparCC

alldomains_df_euxinic <- bind_rows(data.frame(otu_table(ps_bac_euxinic_pruned)), data.frame(otu_table(ps_arch_euxinic_pruned)), data.frame(otu_table(ps_euk_euxinic_pruned)))
alldomains_df_euxinic

Change row names from “denovoXXX” to meaningful names

alldomains_df_full_euxinic <- cbind(ID = rownames(alldomains_df_euxinic), alldomains_df_euxinic)

# start with only first rows, which are bacteria. make one column of meaningful labels
temp1 <- left_join(alldomains_df_full_euxinic[1:dim(otu_table(ps_bac_euxinic_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
temp1$New_ID <- paste(temp1$ID, temp1$"taxonomy-2", temp1$"taxonomy-3", temp1$"taxonomy-4")
temp1 <- select(temp1,-colnames(bac_taxonomy[,2:11]))

# next rows are the archaea 
temp2 <- left_join(alldomains_df_full_euxinic[sum(dim(otu_table(ps_bac_euxinic_pruned))[1],1):sum(dim(otu_table(ps_bac_euxinic_pruned))[1],dim(otu_table(ps_arch_euxinic_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 
temp2$New_ID <- paste(temp2$ID, temp2$"taxonomy-2", temp2$"taxonomy-3")
temp2 <- select(temp2,-colnames(arch_taxonomy[,2:9]))


# last rows are eukarya
euk_taxonomy <- cbind("#ASV ID" = rownames(taxonomy), taxonomy)
temp3 <- left_join(alldomains_df_full_euxinic[sum(dim(otu_table(ps_arch_euxinic_pruned))[1], dim(otu_table(ps_bac_euxinic_pruned))[1],1):sum(dim(otu_table(ps_arch_euxinic_pruned))[1], dim(otu_table(ps_bac_euxinic_pruned))[1],dim(otu_table(ps_euk_euxinic_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
temp3$New_ID <- paste(temp3$ID, temp3$"Supergroup", temp3$"Division", temp3$"Class", temp3$"Order")
temp3 <- select(temp3,-colnames(euk_taxonomy[,2:9]))

# combine back all 3 domains, with new names as row names in a dataframe
alldomains_df_full_euxinic <- rbind(temp1, temp2, temp3)
alldomains_df_full_euxinic <- data.frame(alldomains_df_full_euxinic)
rownames(alldomains_df_full_euxinic) <- alldomains_df_full_euxinic$New_ID
alldomains_df_full_euxinic <- select(alldomains_df_full_euxinic, -c("ID","New_ID"))
alldomains_df_full_euxinic

Remove columns with NAs. These are samples for which the library for at least one domain didn’t work (can’t do correlations with missing values in columns)

alldomains_df_full_euxinic <- alldomains_df_full_euxinic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_full_euxinic

alldomains_df_euxinic <- alldomains_df_euxinic %>%
    select_if(~ !any(is.na(.)))
alldomains_df_euxinic

4 samples remain for correlation

SparCC

SparCC on full dataset

This is largely based on BVCN tutorials NOTE- input for SparCC should be raw count data (after filtering out low-abundance ASVs). The function does a log-ratio transformation to account for compositionality

sparcctable_alldomains <- sparcc(t(alldomains_df))

Put sample names back into result tables

rownames(sparcctable_alldomains$Cor) <- rownames(alldomains_df_full)
colnames(sparcctable_alldomains$Cor) <- rownames(alldomains_df_full)
rownames(sparcctable_alldomains$Cov) <- rownames(alldomains_df_full)
colnames(sparcctable_alldomains$Cov) <- rownames(alldomains_df_full)

sparcctable_alldomains$Cor[1:2,1:2]
                                                                            denovo231149 Proteobacteria Gammaproteobacteria Chromatiales
denovo231149 Proteobacteria Gammaproteobacteria Chromatiales                                                                   1.0000000
denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)                                                    0.6531043
                                                                            denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)
denovo231149 Proteobacteria Gammaproteobacteria Chromatiales                                                                                  0.6531043
denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)                                                                   1.0000000

Plot correlation

plotableSparcc <- sparcctable_alldomains$Cor %>% reorder_cormat %>% get_upper_tri() %>% reshape2::melt() %>% na.omit()

Sparcc_plot <- plotableSparcc %>% ggplot(aes(x = Var2, y = Var1, fill = value)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))

Sparcc_plot

ggsave("figures/sparcc_corr_alldomains.eps",Sparcc_plot, width = 35, height = 35, units = c("in"))

Calculate Sparcc p-values by bootstrapping- TAKES A LONG TIME

tp0 <- proc.time()
out2 <- sparccboot(t(alldomains_df), R = 1000, ncpus = 2)
tp1 <- proc.time()
tp1 - tp0
     user    system   elapsed 
49520.017  3391.854 28336.411 

The above took ~14 hours to run 1000 iterations

Extract p-values




outP <- pval.sparccboot(out2)
data.frame(outP$cors, outP$pvals) %>% head

cors <- outP$cors
pvals <- outP$pvals
sparCCpcors <- diag(0.5, nrow = dim(sparcctable_alldomains$Cor)[1], ncol = dim(sparcctable_alldomains$Cor)[1])
sparCCpcors[upper.tri(sparCCpcors, diag=FALSE)] <- cors
sparCCpcors <- sparCCpcors + t(sparCCpcors)

sparCCpval <- diag(0.5, nrow = dim(sparcctable_alldomains$Cor)[1], ncol = dim(sparcctable_alldomains$Cor)[1])
sparCCpval[upper.tri(sparCCpval, diag=FALSE)] <- pvals
sparCCpval <- sparCCpval + t(sparCCpval)

rownames(sparCCpcors) <- rownames(alldomains_df_full)
colnames(sparCCpcors) <- rownames(alldomains_df_full)
rownames(sparCCpval) <- rownames(alldomains_df_full)
colnames(sparCCpval) <- rownames(alldomains_df_full)

sparCCpcors[1:2, 1:2]
                                                                            denovo231149 Proteobacteria Gammaproteobacteria Chromatiales
denovo231149 Proteobacteria Gammaproteobacteria Chromatiales                                                                   1.0000000
denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)                                                    0.6487105
                                                                            denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)
denovo231149 Proteobacteria Gammaproteobacteria Chromatiales                                                                                  0.6487105
denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)                                                                   1.0000000
sparCCpval[1:2, 1:2]
                                                                            denovo231149 Proteobacteria Gammaproteobacteria Chromatiales
denovo231149 Proteobacteria Gammaproteobacteria Chromatiales                                                                           1
denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)                                                            0
                                                                            denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)
denovo231149 Proteobacteria Gammaproteobacteria Chromatiales                                                                                          0
denovo348086 Deferribacteres Deferribacterales SAR406_clade(Marine_group_A)                                                                           1

Reorder for plotting

reordered_all_sparcc <- reorder_cor_and_p(sparCCpcors, sparCCpval)
reordered_sparccCor <- reordered_all_sparcc$r
reordered_sparccP<- reordered_all_sparcc$p


sparccCor_processed <- reordered_sparccCor  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(cor = value)
sparccP_processed <- reordered_sparccP  %>% get_upper_tri() %>% reshape2::melt() %>% na.omit() %>% rename(p = value)

# join the two data frames

SparccP <- left_join(sparccCor_processed, sparccP_processed, by = c("Var1", "Var2")) %>%
  # # remove self correlations
  # filter(Var1 != Var2) %>% 
  # calculate the false discovery rate to adjust for multiple p values
  mutate(fdr = p.adjust(p, method = "BH"))

And plot correlation with p-values. Circles mean that the relationship is sig. at p = 0.05 level, based on bootstrapping

fdrThresh <- 0.01 # fdr threshold
sparccOkP <- SparccP%>% filter(fdr < fdrThresh) 

SparccP_plot <- SparccP %>% ggplot(aes(x = Var2, y = Var1, fill = cor)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1)) + geom_point(data = sparccOkP, shape = 1)

SparccP_plot

ggsave("figures/sparcc_corr_alldomains_w_pvals.eps",SparccP_plot, width = 20, height = 20, units = c("in"))

Save environment again

save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_sparcc_bootstrap.RData")

Or load if coming back

load("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_sparcc_bootstrap.RData")

SpiecEasi

Try the SpiecEasi method, which accounts for sparse data, as described in the SpiecEasi publication, spieceasi github, and BVCN lessons 1.2. This reduces the clumps (eg. sparse relationships that are secondary or teriary, not direct relationships).

Make functions from tutorial

convertSEToTable <- function(se_out,sp.names){
  #This is just a fancy helper function to get the data in a comparable format to the output of lesson 1 so we can make a similar plot. We will cover other methods for visualizing this type of output in future lessons.
  secor <- cov2cor(as.matrix(getOptCov(se_out))) # See spieceasi documentation for how to pull out weights for comparison
  elist     <- summary(triu(secor*getRefit(se_out), k=1))
  elist[,1] <- sp.names[elist[,1]]
  elist[,2] <- sp.names[elist[,2]]
  elist[,4] <- paste(elist[,1],elist[,2])
  full_e <- expand.grid(sp.names,sp.names)
  rownames(full_e) <- paste(full_e[,1],full_e[,2])
  full_e[,"Weight"] <- 0
  full_e[elist[,4],"Weight"] <- elist[,3]
  x <- expand.grid(1:length(sp.names),1:length(sp.names))
  full_e[x[,"Var1"]>x[,"Var2"],"Weight"] <- NA
  return(as.data.frame(full_e,stringsAsFactors=F))
}

SpiecEasi on full dataset

Follow the spieceasi documentation to find optimal parameters. Also, because I want to compare networks, this convo on using optimal parameters for different network comparisons is helpful.

Remove samples from the phyloseq objects that are not in all 3 domains and reorder samples so they are in same order in all 3 objects

bac_arch_common <- intersect(sample_names(ps_bac_ra_pruned), sample_names(ps_arch_ra_pruned))
all_common <- intersect(bac_arch_common, sample_names(ps_euk_ra_pruned))

ps_bac_pruned <- prune_samples(all_common, ps_bac_pruned)
ps_arch_pruned <- prune_samples(all_common, ps_arch_pruned)
ps_euk_pruned <- prune_samples(all_common, ps_euk_pruned)

ps_bac_ra_pruned <- prune_samples(all_common, ps_bac_ra_pruned)
ps_arch_ra_pruned <- prune_samples(all_common, ps_arch_ra_pruned)
ps_euk_ra_pruned <- prune_samples(all_common, ps_euk_ra_pruned)


otu_table(ps_arch_pruned) <- otu_table(ps_arch_pruned)[,sample_names(ps_bac_ra_pruned)]
otu_table(ps_euk_pruned) <- otu_table(ps_euk_pruned)[,sample_names(ps_bac_ra_pruned)]

sample_data(ps_bac_pruned)
Sample Data:        [36 samples by 66 sample variables]:
sample_data(ps_arch_pruned)
Sample Data:        [36 samples by 66 sample variables]:
sample_data(ps_euk_pruned)
Sample Data:        [36 samples by 66 sample variables]:
#Run Spieceasi
pargs <- list(seed=10010)
se <- spiec.easi(list(ps_bac_pruned, ps_arch_pruned, ps_euk_pruned), method='glasso', lambda.min.ratio=1e-2, nlambda=100, pulsar.params=pargs)
Applying data transformations...
Selecting model with pulsar using stars...
Fitting final estimate with glasso...
done
getStability(se)
[1] 0.04639267

the above takes a while to run (20-30 mins). Using parameters above, the stability along the lambda path crosses the 0.05 threshold and the final stability value (0.044) is sufficiently close to 0.05

#This is just a fancy helper function to get the data in a comparable format to the output of above
tab.se <- convertSEToTable(se,sp.names=colnames(t(alldomains_df_full))) 

#Plot 
plot.se <- ggplot(tab.se,aes(x = Var1, y = Var2, fill = Weight)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
plot(plot.se)

ggsave("figures/spieceasi_alldomains.eps",plot.se, width = 35, height = 35, units = c("in"))

Note- only the significant values above show up in the heatmap above (ie. there is no “p-value”)

SpiecEasi on oxycline depths

bac_arch_common <- intersect(sample_names(ps_bac_oxycline_pruned), sample_names(ps_arch_oxycline_pruned))
all_common <- intersect(bac_arch_common, sample_names(ps_euk_oxycline_pruned))

ps_bac_oxycline_pruned <- prune_samples(all_common, ps_bac_oxycline_pruned)
ps_arch_oxycline_pruned <- prune_samples(all_common, ps_arch_oxycline_pruned)
ps_euk_oxycline_pruned <- prune_samples(all_common, ps_euk_oxycline_pruned)


otu_table(ps_arch_oxycline_pruned) <- otu_table(ps_arch_oxycline_pruned)[,sample_names(ps_bac_oxycline_pruned)]
otu_table(ps_euk_oxycline_pruned) <- otu_table(ps_euk_oxycline_pruned)[,sample_names(ps_bac_oxycline_pruned)]

sample_data(ps_bac_oxycline_pruned)
Sample Data:        [21 samples by 66 sample variables]:
sample_data(ps_arch_oxycline_pruned)
Sample Data:        [21 samples by 66 sample variables]:
sample_data(ps_euk_oxycline_pruned)
Sample Data:        [21 samples by 66 sample variables]:
#Run Spieceasi
pargs <- list(seed=10010)
se.oxycline <- spiec.easi(list(ps_bac_oxycline_pruned, ps_arch_oxycline_pruned, ps_euk_oxycline_pruned), method='glasso', lambda.min.ratio=5e-3, nlambda=300, pulsar.params=pargs)
Applying data transformations...
Selecting model with pulsar using stars...
Fitting final estimate with glasso...
done
getStability(se.oxycline)
[1] 0.04467512

the above takes a couple of minutes to run. Stability and stability along lambda path are very similar to the full dataset spieceasi object (se) with these parameters above. Continue with these.

# Pull out spp names from oxcyline phyloseq objects and concatenate


#This is just a fancy helper function to get the data in a comparable format to the output of above
tab.se.oxycline <- convertSEToTable(se.oxycline, sp.names=colnames(t(alldomains_df_full_oxycline))) 

#Plot 
plot.se.oxycline <- ggplot(tab.se.oxycline,aes(x = Var1, y = Var2, fill = Weight)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
plot(plot.se.oxycline)

ggsave("figures/spieceasi_alldomains_oxycline.eps",plot.se.oxycline, width = 35, height = 35, units = c("in"))

SpiecEasi on anoxic depths

bac_arch_common <- intersect(sample_names(ps_bac_anoxic_pruned), sample_names(ps_arch_anoxic_pruned))
There were 15 warnings (use warnings() to see them)
all_common <- intersect(bac_arch_common, sample_names(ps_euk_anoxic_pruned))

ps_bac_anoxic_pruned <- prune_samples(all_common, ps_bac_anoxic_pruned)
ps_arch_anoxic_pruned <- prune_samples(all_common, ps_arch_anoxic_pruned)
ps_euk_anoxic_pruned <- prune_samples(all_common, ps_euk_anoxic_pruned)


otu_table(ps_arch_anoxic_pruned) <- otu_table(ps_arch_anoxic_pruned)[,sample_names(ps_bac_anoxic_pruned)]
otu_table(ps_euk_anoxic_pruned) <- otu_table(ps_euk_anoxic_pruned)[,sample_names(ps_bac_anoxic_pruned)]

sample_data(ps_bac_anoxic_pruned)
Sample Data:        [11 samples by 66 sample variables]:
sample_data(ps_arch_anoxic_pruned)
Sample Data:        [11 samples by 66 sample variables]:
sample_data(ps_euk_anoxic_pruned)
Sample Data:        [11 samples by 66 sample variables]:
#Run Spieceasi
pargs <- list(seed=10010)
se.anoxic <- spiec.easi(list(ps_bac_anoxic_pruned, ps_arch_anoxic_pruned, ps_euk_anoxic_pruned), method='glasso', lambda.min.ratio=1e-1, nlambda=300, pulsar.params=pargs)
Applying data transformations...
Selecting model with pulsar using stars...
Fitting final estimate with glasso...
done
getStability(se.anoxic)
[1] 0.04968652

the above takes a couple of minutes to run

#This is just a fancy helper function to get the data in a comparable format to the output of above
tab.se.anoxic <- convertSEToTable(se.anoxic, sp.names=colnames(t(alldomains_df_full_anoxic))) 

#Plot 
plot.se.anoxic <- ggplot(tab.se.anoxic,aes(x = Var1, y = Var2, fill = Weight)) + geom_tile() + scale_fill_gradient2() + theme(axis.text.x = element_text(angle = 90, hjust = 1))
plot(plot.se.anoxic)

ggsave("figures/spieceasi_alldomains_anoxic.eps",plot.se.anoxic, width = 35, height = 35, units = c("in"))

SpiecEasi on euxinic depths

bac_arch_common <- intersect(sample_names(ps_bac_euxinic_pruned), sample_names(ps_arch_euxinic_pruned))
all_common <- intersect(bac_arch_common, sample_names(ps_euk_euxinic_pruned))

ps_bac_euxinic_pruned <- prune_samples(all_common, ps_bac_euxinic_pruned)
ps_arch_euxinic_pruned <- prune_samples(all_common, ps_arch_euxinic_pruned)
ps_euk_euxinic_pruned <- prune_samples(all_common, ps_euk_euxinic_pruned)


otu_table(ps_arch_euxinic_pruned) <- otu_table(ps_arch_euxinic_pruned)[,sample_names(ps_bac_euxinic_pruned)]
otu_table(ps_euk_euxinic_pruned) <- otu_table(ps_euk_euxinic_pruned)[,sample_names(ps_bac_euxinic_pruned)]

sample_data(ps_bac_euxinic_pruned)
Sample Data:        [4 samples by 66 sample variables]:
sample_data(ps_arch_euxinic_pruned)
Sample Data:        [4 samples by 66 sample variables]:
sample_data(ps_euk_euxinic_pruned)
Sample Data:        [4 samples by 66 sample variables]:
#Run Spieceasi
pargs <- list(seed=10010)
se.euxinic <- spiec.easi(list(ps_bac_euxinic_pruned, ps_arch_euxinic_pruned, ps_euk_euxinic_pruned), method='glasso', lambda.min.ratio=1e-5,nlambda=20, pulsar.params=pargs)
Applying data transformations...
Selecting model with pulsar using stars...
Optimal lambda may be larger than the supplied valuesFitting final estimate with glasso...
done
getStability(se.euxinic)
[1] 0.06180995

I tried many parameters on the above but cannot get a satisfactory solution. There are just too few samples after quality filtering to do SpiecEasi on the euxinic depths only.

Save and re-load environment

save.image("EnvironmentBackups/CariacoEuks_postanalysis_vars_upto_spieceasi.RData")

Or load if coming back

There were 14 warnings (use warnings() to see them)

Network Analysis

Build using igraph

All depths and all domains

3 Domain Network- Oxycline

#Extract adjacency matrix from spiecEasi output
adj.mat <- getRefit(se.oxycline)
table(as.numeric(adj.mat))

    0     1 
35241  1240 
# Extract weighted adjacency
se.cor  <- cov2cor(as.matrix(getOptCov(se.oxycline)))
weighted.adj.mat <- se.cor*getRefit(se.oxycline)

#Convert to graph objects
grph.unweighted.oxycline <- adj2igraph(adj.mat)
grph.oxycline <- adj2igraph(weighted.adj.mat)


# Put back in species names
V(grph.oxycline)$name <- rownames(alldomains_df_full_oxycline)
# V(grph.oxycline)

# Make size of nodes proportional to degree (number of connections)
V(grph.oxycline)$size <- (degree(grph.oxycline) + 1) # the +1 avoids size zero vertices

# Color edges by connection (positive or negative) 
# E(grph.oxycline)$color <- custombluegreen
# E(grph.oxycline)$color[E(grph.oxycline)$weight<0] <- customreddishpurple

# Change width of edges to be proportional to their weights
E(grph.oxycline)$width <- abs(E(grph.oxycline)$weight)*10

# Scale node sizes to be smaller
V(grph.oxycline)$size <- V(grph.oxycline)$size/2

# Remove low-weight edges (you decide what threshold is right for your network):
# weight_threshold <- 0.07
# grph.oxycline <- delete.edges(grph.oxycline,which(abs(E(grph.oxycline)$weight)<weight_threshold))

# Color nodes by domain
dtype <- c(rep("red",ntaxa(ps_bac_oxycline_pruned)), rep("green",ntaxa(ps_arch_oxycline_pruned)), rep("blue",ntaxa(ps_euk_oxycline_pruned)))

# Plot
plot(grph.oxycline,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline),
     vertex.color=dtype)
title("SpiecEasi Network: All domains, Oxycline")
legend("topright",bty = "n",
       legend=c("Bacteria","Archaea", "Eukarya"),
       fill=c("red","green","blue"), border=NA)

# Save plot
setEPS()
postscript(file = "Figures/3domains_oxycline_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.oxycline,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.oxycline),
     vertex.color=dtype)
title("SpiecEasi Network: All domains, Oxycline")
legend("topright",bty = "n",
       legend=c("Bacteria","Archaea", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 

3 Domain Network- Anoxic

#Extract adjacency matrix from spiecEasi output
adj.mat <- getRefit(se.anoxic)
table(as.numeric(adj.mat))

   0    1 
7566  178 
# Extract weighted adjacency
se.cor  <- cov2cor(as.matrix(getOptCov(se.anoxic)))
weighted.adj.mat <- se.cor*getRefit(se.anoxic)

#Convert to graph objects
grph.unweighted.anoxic <- adj2igraph(adj.mat)
grph.anoxic <- adj2igraph(weighted.adj.mat)


# Put back in species names
V(grph.anoxic)$name <- rownames(alldomains_df_full_oxycline)
number of items to replace is not a multiple of replacement length
# V(grph.anoxic)

# Make size of nodes proportional to degree (number of connections)
V(grph.anoxic)$size <- (degree(grph.anoxic) + 1) # the +1 avoids size zero vertices

# Color edges by connection (positive or negative) 
# E(grph.anoxic)$color <- custombluegreen
# E(grph.anoxic)$color[E(grph.anoxic)$weight<0] <- customreddishpurple

# Change width of edges to be proportional to their weights
E(grph.anoxic)$width <- abs(E(grph.anoxic)$weight)*10

# Scale node sizes to be smaller
V(grph.anoxic)$size <- V(grph.anoxic)$size/2

# Remove low-weight edges (you decide what threshold is right for your network):
# weight_threshold <- 0.07
# grph.anoxic <- delete.edges(grph.anoxic,which(abs(E(grph.anoxic)$weight)<weight_threshold))

# Color nodes by domain
dtype <- c(rep("red",ntaxa(ps_bac_anoxic_pruned)), rep("green",ntaxa(ps_arch_anoxic_pruned)), rep("blue",ntaxa(ps_euk_anoxic_pruned)))

# Plot
plot(grph.anoxic,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic),
     vertex.color=dtype)
title("SpiecEasi Network: All domains, Anoxic Layer")
legend("topright",bty = "n",
       legend=c("Bacteria","Archaea", "Eukarya"),
       fill=c("red","green","blue"), border=NA)

# Save plot
setEPS()
postscript(file = "Figures/3domains_anoxic_spieceasi_network.eps", width = 5.5, height = 5)
plot(grph.anoxic,
     vertex.label=NA,
     layout=layout_with_graphopt(grph.anoxic),
     vertex.color=dtype)
title("SpiecEasi Network: All domains, Anoxic")
legend("topright",bty = "n",
       legend=c("Bacteria","Archaea", "Eukarya"),
       fill=c("red","green","blue"), border=NA)
dev.off()
quartz_off_screen 
                2 

Collect network parameters

Number of edges

the number of edges and how many are positive vs negative

# total number of edges in full dataset network
length(E(grph)$weight)
[1] 2840
# percent of neg edges 
(sum(E(grph)$weight<0)/length(E(grph)$weight))*100
[1] 34.40141
# total number of edges in oxycline network
length(E(grph.oxycline)$weight)
[1] 620
# percent of neg edges 
(sum(E(grph.oxycline)$weight<0)/length(E(grph.oxycline)$weight))*100
[1] 37.74194
# total number of edges in oxycline network
length(E(grph.anoxic)$weight)
[1] 89
# percent of neg edges 
(sum(E(grph.anoxic)$weight<0)/length(E(grph.anoxic)$weight))*100
[1] 34.83146

Declining number of total edges going from full dataset –> oxycline only –> anoxic only. But the percentage of negative associations is similar (34.4-37.7%). Most associations (~65%) in each network are positive.

Edge density

the number of edges relatives to total number of possible edges

edge_density(grph)
[1] 0.06374717
edge_density(grph.oxycline)
[1] 0.03416919
edge_density(grph.anoxic)
[1] 0.02324974

The full dataset has the highest edge density, then oxycline, then anoxic

Component

The size of the components, or “clumps,” in the network, and how many members in each

# full dataset
components(grph)$no
[1] 27
components(grph)$csize
 [1] 262   9   1   1   1   1   1   1   2   1   1   1   1   1   1   1   1   3   1   1   1   1   1   1   1   1   1
# oxycline
components(grph.oxycline)$no
[1] 32
components(grph.oxycline)$csize
 [1] 144   1   2   1   1   1   1   1   1   1   1   1   1   1   2   2  11   1   2   1   1   1   1   1   1   1   1   1   3
[30]   1   1   1
# anoxic
components(grph.anoxic)$no
[1] 48
components(grph.anoxic)$csize
 [1] 24  2  2  1  1  1  1  1  1  1  4  1  1  1  1  2  1  3  3  1  1  2  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
[40]  4  1  1  1  4  1  1  1  1

The anoxic network is most disjointed, with 48 clumps and the largest containing only 24 members. The next is oxycline, with 32 clumps and the largest with 144 members. Then the full dataset has only 27 clumps and the largest clump contains 262 members.

Average path length

Path is the shortest distance between two nodes (fewest number of edges). Average path length of a network gives a sense of how connected every node is to another. Unconnected hubs in the netowrk will have “infinite” paths from other hubs. The function mean_distance ignores the infinite edges and calculates the average of all other edges

mean_distance(grph)
[1] 3.353247
mean_distance(grph.oxycline)
[1] 3.976057
mean_distance(grph.anoxic)
[1] 2.703947

The longest average path length is in the oxycline, followed by the whole dataset and then anoxic. Meaning the nodes in the anoxic are more closely associated with each other. Even though there are more hubs in anoxic, as shown above, the nodes in the hubs are close to each other. The oxycline hubs have the longest average distances between nodes.

Analyze node-level measures from each network

Calculate 4 parameters for each individual node:

  • degree (de) = number of edges connected to the node
  • strength (st) = sum of the weights of all the edges connected to the node
  • betweenness centrality (be) = the number of shortest paths (between all other nodes) that go through the node
  • closeness centrality (cc) = average distance (number of edges) of the node to any other node
  • local clustering coefficient (l.cluster) , aka transitivity = the propoortion of two nodes that are connected by a third node, of being connected to each other (eg. how many of a node’s “friends” know each other?)

All depth network-

# First change the weights of the edges (the strength of association) to absolute value. This won't work if negative associations are left with negative signs
E(grph)$weight <- abs(E(grph)$weight)

names=V(grph)$name
de=degree(grph)
st=graph.strength(grph)
be=betweenness(grph, normalized=T)
cc = closeness(grph)
At centrality.c:2617 :closeness centrality is not well-defined for disconnected graphs
l.cluster=transitivity(grph, "local")



# assemble dataset and match full taxonomy
fulldateset_node_measures <- data.frame(ID=names, degree=de, strength=st, betweenness=be, closeness = cc, clustering_coefficient = l.cluster) 

# Put back bac taxaonomy
temp1 <- left_join(fulldateset_node_measures[1:dim(otu_table(ps_bac_pruned))[1],], bac_taxonomy, by = c("ID" = "#OTU ID")) 
# delete "Taxonomy-9" and "refined Taxonomy" columns 
temp1 <- select(temp1, -"taxonomy-9", -"Refined taxonomy")


temp2 <- left_join(fulldateset_node_measures[sum(dim(otu_table(ps_bac_pruned))[1],1):sum(dim(otu_table(ps_bac_pruned))[1],dim(otu_table(ps_arch_pruned))[1]),], arch_taxonomy, by = c("ID" = "#OTU ID")) 


temp3 <- left_join(fulldateset_node_measures[sum(dim(otu_table(ps_arch_pruned))[1], dim(otu_table(ps_bac_pruned))[1],1):sum(dim(otu_table(ps_arch_pruned))[1], dim(otu_table(ps_bac_pruned))[1],dim(otu_table(ps_euk_pruned))[1]),], euk_taxonomy, by = c("ID" = "#ASV ID")) 
# Rename col names to match those from Bac and Arch
temp3 <- temp3 %>%
  rename("taxonomy-1" = Kingdom, "taxonomy-2" = Supergroup, "taxonomy-3" = Division, "taxonomy-4" = Class, "taxonomy-5" = Order, "taxonomy-6" = Family, "taxonomy-7" = Genus, "taxonomy-8" = Species)

# combine back all 3 domains, with new names as row names in a dataframe
fulldateset_node_measures <- rbind(temp1, temp2, temp3)
fulldateset_node_measures

Plot betweeness vs degree for each node. - Tipton et al. argue that nodes with high betweenness are “bottlenecks” or important connectors and nodes with high degree are “hubs” - Berry et al. argue that nodes with low betweenness, high degree, high closeness, and high transitivity are candidate keystone species - Add in closeness into the node’s plotly label since these don’t vary much node-to-node and wouldn’t make sense to plot

# replace NA in taxonomy with unidentified 
There were 15 warnings (use warnings() to see them)
# remove nodes with 0 betweenness (can't calculate log10 of 0)
# replace NaN clustering coefs with 0
fulldateset_node_measures <- fulldateset_node_measures %>% 
  replace(is.na(.), "unidentified") %>%
  filter(!betweenness == 0) 

# get enough colors and randomly rearrange so they are easier to separate on the plot
mycolors <- colorRampPalette(brewer.pal(12, "Paired"))(length(unique(fulldateset_node_measures$`taxonomy-3`)))
mycolors <-  sample(mycolors)

# plot with plotly and so I can hover over points and determine which taxa they are
p <- ggplot(fulldateset_node_measures, aes(x = degree, y = betweenness, ID = ID, shape = `taxonomy-1`, `taxonomy-2` = `taxonomy-2`, color = `taxonomy-3`, `taxonomy-4` = `taxonomy-4`, `taxonomy-5` = `taxonomy-5`)) +
  geom_point(size = 5) +
  scale_y_continuous(trans='log10') +
  scale_color_manual(values = mycolors) +
  theme_bw()
ggplotly(p, tooltip = c("ID","taxonomy-2", "taxonomy-3", "taxonomy-4", "taxonomy-5"))

NA

Plot node degree and transitivity (clustering coefficient)

# plot with plotly and so I can hover over points and determine which taxa they are
There were 14 warnings (use warnings() to see them)
p <- ggplot(fulldateset_node_measures, aes(x = clustering_coefficient, y = betweenness, ID = ID, shape = `taxonomy-1`, `taxonomy-2` = `taxonomy-2`, color = `taxonomy-3`, `taxonomy-4` = `taxonomy-4`, `taxonomy-5` = `taxonomy-5`)) +
  geom_point(size = 5) +
  scale_color_manual(values = mycolors) +
    scale_y_continuous(trans='log10') +
  theme_bw()
ggplotly(p, tooltip = c("ID","taxonomy-2", "taxonomy-3", "taxonomy-4", "taxonomy-5"))

NA

STOPPED HERE. CONTINUE PLAYING WITH PLOT ABOVE, highliting specific groups (Synd, Spumellaride, Alphas, Deferribac, etc). DO SAME FOR OXYCLINE AND ANOXIC ONLY (WILL NEED TO MAKE EDITS IN FILTERING ABOVE FOR CLEAR NAMES). CONTINUE TO COLLECT OVERALL NETWORK PARAMETERS FOLLOWING: https://dshizuka.github.io/networkanalysis/04_measuring.html#betweenness. REMOVE EUKS TO SEE HOW NETOWORKS CHANGE

  • report blast/ new SILVA results for
    • “No blast hit” denovo129797
    • “AEGAN-245” from a metagenome denovo351910
    Figure out how to calculate closeness for only major hub, rather than whole
LS0tCnRpdGxlOiAiQ2FyaWFjb19FdWtzX1Bvc3RBbmFseXNpcyIKYXV0aG9yOiAiTGl6IFN1dGVyIgpkYXRlOiAiNS8yNi8yMDIwIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgojIExvYWQgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocGh5bG9zZXEpCmxpYnJhcnkocGhhbmdvcm4pCmxpYnJhcnkocmVhZHIpCiNsaWJyYXJ5KHNlcWlucikKbGlicmFyeShhcGUpCmxpYnJhcnkodmVnYW4pCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KG1pY3JvYmlvbWUpCmxpYnJhcnkoY29tcG9zaXRpb25zKQpsaWJyYXJ5KFNwaWVjRWFzaSkKbGlicmFyeShvdHVTdW1tYXJ5KQpsaWJyYXJ5KHBzeWNoKQpsaWJyYXJ5KE1hdHJpeCkKbGlicmFyeShpZ3JhcGgpCmxpYnJhcnkocGxvdGx5KQoKCiMgSGVscGVyIGZ1bmN0aW9ucyBmcm9tIEouIENyYW0gaHR0cHM6Ly9iaW92Y25ldC5naXRodWIuaW8vX3BhZ2VzL05ldHdvcmtTY2llbmNlX1NwYXJDQy5uYgpwYXNzIDwtIGZ1bmN0aW9uKHgpe3h9IAojIEdldCBsb3dlciB0cmlhbmdsZSBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4CiAgZ2V0X2xvd2VyX3RyaTwtZnVuY3Rpb24oY29ybWF0KXsKICAgIGNvcm1hdFt1cHBlci50cmkoY29ybWF0KV0gPC0gTkEKICAgIHJldHVybihjb3JtYXQpCiAgfQojIEdldCB1cHBlciB0cmlhbmdsZSBvZiB0aGUgY29ycmVsYXRpb24gbWF0cml4CiAgZ2V0X3VwcGVyX3RyaSA8LSBmdW5jdGlvbihjb3JtYXQpewogICAgY29ybWF0W2xvd2VyLnRyaShjb3JtYXQpXTwtIE5BCiAgICByZXR1cm4oY29ybWF0KQogIH0KCnJlb3JkZXJfY29ybWF0IDwtIGZ1bmN0aW9uKGNvcm1hdCl7CiMgVXNlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmFyaWFibGVzIGFzIGRpc3RhbmNlCmRkIDwtIGFzLmRpc3QoKDEtY29ybWF0KS8yKQpoYyA8LSBoY2x1c3QoZGQpCmNvcm1hdCA8LWNvcm1hdFtoYyRvcmRlciwgaGMkb3JkZXJdCn0KCnJlb3JkZXJfY29yX2FuZF9wIDwtIGZ1bmN0aW9uKGNvcm1hdCwgcG1hdCl7CiAgZGQgPC0gYXMuZGlzdCgoMS1jb3JtYXQpLzIpCiAgaGMgPC0gaGNsdXN0KGRkKQogIGNvcm1hdCA8LWNvcm1hdFtoYyRvcmRlciwgaGMkb3JkZXJdCiAgcG1hdCA8LSBwbWF0W2hjJG9yZGVyLCBoYyRvcmRlcl0KICBsaXN0KHIgPSBjb3JtYXQsIHAgPSBwbWF0KQp9CgojQ3VzdG9tIGNvbG9yYmxpbmQgcGFsbGV0dGUsIHNlZTogaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNTcxNTM0Mjgvci1wbG90LWNvbG9yLWNvbWJpbmF0aW9ucy10aGF0LWFyZS1jb2xvcmJsaW5kLWFjY2Vzc2libGUKY3VzdG9tdmVybWlsbGlvbjwtcmdiKDIxMy8yNTUsOTQvMjU1LDAvMjU1KQpjdXN0b21ibHVlZ3JlZW48LXJnYigwLzI1NSwxNTgvMjU1LDExNS8yNTUpCmN1c3RvbWJsdWU8LXJnYigwLzI1NSwxMTQvMjU1LDE3OC8yNTUpCmN1c3RvbXNreWJsdWU8LXJnYig4Ni8yNTUsMTgwLzI1NSwyMzMvMjU1KQpjdXN0b21yZWRkaXNocHVycGxlPC1yZ2IoMjA0LzI1NSwxMjEvMjU1LDE2Ny8yNTUpIAoKIyBSZXBvcnQgdmVyc2lvbnMgb2YgcGFja2FnZXMKc2Vzc2lvbkluZm8oKQpgYGAKCi0gTm90ZSBmb3IgU3BpZWNFYXNpLCBpbnN0YWxsYXRpb24gaW5zdHJ1Y3Rpb25zIGFyZSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3pkazEyMy9TcGllY0Vhc2kpLiBPbiBNYWMsIHRoZXJlIGFyZSBpc3N1ZXMgd2l0aCBhIGRlcGVuZGVuY3kgd2hpY2ggSSBhbHNvIGhhZCB0byB1cGRhdGUgdXNpbmcgdGhlc2UgW2luc3RydWN0aW9uc10oaHR0cHM6Ly90aGVjb2F0bGVzc3Byb2Zlc3Nvci5jb20vcHJvZ3JhbW1pbmcvY3BwL3ItY29tcGlsZXItdG9vbHMtZm9yLXJjcHAtb24tbWFjb3MvKQoKCiMgUHJlcGFyZSB0aGUgZGF0YQoKIyMgSW1wb3J0IAoKTWV0YWRhdGE6CmBgYHtyfQptZXRhZGF0YSA8LSByZWFkX2NzdigiTWV0YWRhdGEuY3N2IikKYGBgCgpJbXBvcnQgU1JBIHRhYmxlIGFuZCBtYXRjaCBTUkEgSURzIHdpdGggc2FtcGxlIElEcyBpbiBtZXRhZGF0YSBmaWxlCmBgYHtyfQpTUkFSdW5UYWJsZSA8LSByZWFkX2Nzdigic3JhX2RhdGEvU3JhUnVuVGFibGUudHh0IikKbWV0YWRhdGEgPC0gbGVmdF9qb2luKG1ldGFkYXRhLCBTUkFSdW5UYWJsZSwgYnkgPSAnU2FtcGxlIE5hbWUnKQpgYGAKCgoKREFEQTIgcmVzdWx0czoKYGBge3J9CiMgSW1wb3J0IENvdW50IHRhYmxlLiBTa2lwIGZpcnN0IHJvdyBvZiB0c3YgZmlsZSwgd2hpY2ggaXMganVzdCBzb21lIHRleHQKY291bnRfdGFibGUgPC0gcmVhZF90c3YoZmlsZT0iZGFkYTJfZXhwb3J0L0FTVnNfY291bnRzLnRzdiIpCiMgQW5kIHNwZWNpZnkgdGhhdCB0aGUgZmlyc3QgY29sdW1uIG9mIGRhdGEgYXJlIHJvd25hbWVzCmNvdW50X3RhYmxlIDwtIGNvbHVtbl90b19yb3duYW1lcyhjb3VudF90YWJsZSwgdmFyID0gY29sbmFtZXMoY291bnRfdGFibGUpWzFdKQoKIyBJbXBvcnQgdGF4b25vbXkgb2YgQVNWcwp0YXhvbm9teSA8LSByZWFkX3RzdihmaWxlPSJkYWRhMl9leHBvcnQvQVNWc190YXhvbm9teS50c3YiKQojIEFuZCBzcGVjaWZ5IHRoYXQgdGhlIGZpcnN0IGNvbHVtbiBvZiBkYXRhIGFyZSByb3duYW1lcwp0YXhvbm9teSA8LSBjb2x1bW5fdG9fcm93bmFtZXModGF4b25vbXksIHZhciA9IGNvbG5hbWVzKHRheG9ub215KVsxXSkKYGBgCgoKIyMgUUMgYW5kIEZpbHRlcmluZwoKIyMjIFJhcmVmYWN0aW9uIGN1cnZlcwoKYGBge3J9CiMgVXNlIHJhcmVjdXJ2ZSwgZnJvbSB0aGUgVmVnYW4gcGFja2FnZS4gUmFyY3VydmUgZXhwZWN0cyB0aGUgZGF0YXNldCBhcyBhIGRhdGFmcmFtZSBzbyB3ZSBuZWVkIHRvIHVzZSBhcy5kYXRhLmZyYW1lIGFnYWluOgpjb3VudF90YWJsZV9kZiA8LSBhcy5kYXRhLmZyYW1lKGNvdW50X3RhYmxlKQoKIyBQbG90IHRoZSByYXJlZmFjdGlvbiBjdXJ2ZXMsIGNvbG9yLWNvZGluZyBieSB0aGUgY29sb3JzIGxpc3RlZCBpbiBzYW1wbGVfaW5mb190YWIsIHdoaWNoIGluZGljYXRlIHNhbXBsZSB0eXBlLCBhbmQgdHJhbnNmb3JtaW5nIHVzaW5nIHQoKSBhZ2FpbgojIFJ1bm5pbmcgdGhpcyA1LTEwIHNhbXBsZXMgYXQgYSB0aW1lIGJlY2F1c2Ugb3RoZXJ3aXNlIGl0IHRha2VzIGEgbG9uZyB0aW1lIHRvIHJlbmRlcgpyYXJlY3VydmUodChjb3VudF90YWJsZV9kZiksIHN0ZXA9MTAwLCBjZXg9MC41LCB5bGFiPSJBU1ZzIiwgbGFiZWw9VCkKYGBgCgoKIyMjIFJlbW92ZSBzaW5nbGV0b25zCgpgYGB7cn0KY291bnRfdGFibGVfbm9fc2luZ2xldG9ucyA8LSBmaWx0ZXIoY291bnRfdGFibGUscm93U3Vtcyhjb3VudF90YWJsZSk+MSkKIyByZXRhaW5zIGFsbCBBU1ZzIChvdXQgb2YgMTQxNzYpCmBgYAoKYW5kIGNoYW5nZSBzYW1wbGUgbmFtZXMgZnJvbSBOQ0JJIElEIHRvIG91ciBpbnRlcm5hbCBzYW1wbGUgSURzCmBgYHtyfQojIE1vZGlmeSB0YXhhIG5hbWVzIGluIGNvdW50X3RhYmxlX25vX3NpbmdsZXRvbnMsIHdoaWNoIGFyZSB0aGUgTkNCSSBTUkEgbnVtYmVycy4gV2FudCB0byB1c2Ugb3VyIGludGVybmFsIHNhbXBsZSBrZXkKa2V5IDwtIFNSQVJ1blRhYmxlICU+JSBzZWxlY3QoUnVuLCAnU2FtcGxlIE5hbWUnKQoKeCA8LSAodChjb3VudF90YWJsZV9ub19zaW5nbGV0b25zKSkKeCA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKHgsIFJ1biA9IHJvd25hbWVzKHgpKSkKCnkgPC0gdChsZWZ0X2pvaW4oeCwga2V5LCBieSA9ICJSdW4iKSkKY29sbmFtZXMoeSkgPC0geVsnU2FtcGxlIE5hbWUnLF0KeSA8LSB5WyAhKHJvd25hbWVzKHkpICVpbiUgYygnU2FtcGxlIE5hbWUnLCAnUnVuJykpLCBdCgpjb3VudF90YWJsZV8yIDwtIHR5cGVfY29udmVydChhcy5kYXRhLmZyYW1lKHkpKQpgYGAKCiMjIyBNYWtlIFBoeWxvIFRyZWUgIApUaGlzIHByb2Nlc3MgdGFrZXMgYSBMT05HIHRpbWUgc28gcnVuIG9uY2UgYW5kIHNhdmUgLlJEYXRhIG9iamVjdApJbiB0aGUgRGFkYTIgdG9vbHMsIHRoZXJlIGFyZSBubyBvcHRpb25zIHRvIGJ1aWxkIGEgdHJlZSAodW5saWtlIGluIFFpaW1lMikgYnV0IHdlIGNhbiBidWlsZCBpdCBoZXJlIHVzaW5nIERFQ0lQSEVSIGFuZCBwaGFuZ29ybgoKKEJhc2VkIG9uIFtodHRwczovL2YxMDAwcmVzZWFyY2guY29tL2FydGljbGVzLzUtMTQ5Mi92Ml0oaHR0cHM6Ly9mMTAwMHJlc2VhcmNoLmNvbS9hcnRpY2xlcy81LTE0OTIvdjIpKQoKCk1ha2UgYW4gYWxpZ25tZW50IHVzaW5nIHRvb2xzIGZyb20gRGVjaXBoZXIgKE5vdGUtIGFsaWdubWVudCBzdGVwIHRha2VzIHNldmVyYWwgaG91cnMuIENvbW1lbnRlZCBvdXQgZm9yIG5vdy4gT25seSBuZWVkIHRvIHJ1biBvbmNlKSAKYGBge3J9CiMjIGltcG9ydCBmYXN0YQojIGZhcyA8LSAiZGFkYTJfZXhwb3J0L0FTVnMuZmEiCiMgc2VxcyA8LSByZWFkRE5BU3RyaW5nU2V0KGZhcykKIyBzZXFzCiMgCiMgIyBwZXJmb3JtIHRoZSBhbGlnbm1lbnQKIyBhbGlnbmVkIDwtIEFsaWduU2VxcyhzZXFzKSAjIGF1dG9tYXRpY2FsbHkgZGV0ZWN0cyBhbmQgdXNlcyBhbGwgY29yZXMKIyAKIyAjIHZpZXcgdGhlIGFsaWdubWVudCBpbiBhIGJyb3dzZXIgKG9wdGlvbmFsKQojIEJyb3dzZVNlcXMoYWxpZ25lZCwgaGlnaGxpZ2h0PTApCiMgCiMgIyB3cml0ZSBvdXQgYWxpZ25lZCBzZXF1ZW5jZSBmaWxlCiMgd3JpdGVYU3RyaW5nU2V0KGFsaWduZWQsIGZpbGU9IkFTVnMuYWxpZ25lZC5mYXN0YSIpCmBgYAoKClVzZSBwaGFuZ29ybiBwYWNrYWdlIHRvIGJ1aWxkIHRyZWUuIEhlcmUgd2UgYXJlIGJ1aWxkaW5nIGEgbWF4aW11bSBsaWtlbGlob29kIG5laWdoYm9yLWpvaW5pbmcgdHJlZS4gKEFsc28gdGFrZXMgYSB3aGlsZSB0byBydW4uIENvbW1lbnQgb3V0IGZvciBub3cuKQoKYGBge3J9CiMgcGhhbmcuYWxpZ24gPC0gcGh5RGF0KGFzKGFsaWduZWQsICJtYXRyaXgiKSwgdHlwZT0iRE5BIikgIyBjb252ZXJ0IHRvIHBoeURhdCBmb3JtYXQKIyBkbSA8LSBkaXN0Lm1sKHBoYW5nLmFsaWduKSAjIGNhbGN1bGF0ZSBwYWlyd2lzZSBkaXN0YW5jZSBtYXRyaXgKIyB0cmVlTkogPC0gTkooZG0pICMgcGVyZm9ybSBuZWlnaGJvci1qb2luaW5nIHRyZWUgbWV0aG9kCiMgZml0ID0gcG1sKHRyZWVOSiwgZGF0YT1waGFuZy5hbGlnbikgIyBjb21wdXRlIGludGVybWFsIG1heCBsaWtlbGlob29kCmBgYAoKIyMjIyBTYXZlIGFuZCByZS1sb2FkIGRhdGFzZXQKU2luY2UgdGhlIHN0ZXAgYWJvdmUgdGFrZXMgYSBsb25nIHRpbWUsIHNhdmUgYWxsIHZhcmlhYmxlcyB1cCB0byB0aGlzIHBvaW50IGluIGVudmlyb25tZW50IGFzIFJEYXRhIG9iamVjdApgYGB7cn0KIyBzYXZlLmltYWdlKCJFbnZpcm9ubWVudEJhY2t1cHMvQ2FyaWFjb0V1a3NfcG9zdGFuYWx5c2lzX3ZhcnNfdXB0b190cmVlLlJEYXRhIikKYGBgCgpSZS1sb2FkCmBgYHtyfQpsb2FkKCJFbnZpcm9ubWVudEJhY2t1cHMvQ2FyaWFjb0V1a3NfcG9zdGFuYWx5c2lzX3ZhcnNfdXB0b190cmVlLlJEYXRhIikKYGBgCgoKIyMjIE1ha2UgcGh5bG9zZXEgb2JqZWN0cwoKSGVyZSB3ZSB3aWxsIGRvIG9yZGluYXRpb25zIHVzaW5nIHRoZSBwaHlsb3NlcSBwYWNrYWdlLCB3aGljaCBmaXJzdCByZXF1aXJlcyBtYWtpbmcgcGh5bG9zZXEgb2JqZWN0cyBvdXQgb2YgZWFjaCBvZiBvdXIgaW5wdXQgZGF0YSB0YWJsZXMgKGluIHRoZSBsYXN0IHR1dG9yaWFsLCBJIGltcG9ydGVkIHRoZSB0cmVlIHVzaW5nIHBoeWxvc2VxIHNvIGl0IGlzIGFscmVhZHkgYSBwaHlsb3NlcSBvYmplY3QpCgpgYGB7cn0KQVNWCT0Jb3R1X3RhYmxlKGNvdW50X3RhYmxlXzIsIHRheGFfYXJlX3Jvd3MgPSAgVFJVRSkKVEFYCT0JdGF4X3RhYmxlKGFzLm1hdHJpeCh0YXhvbm9teSkpCk1FVEEJPQlzYW1wbGVfZGF0YShkYXRhLmZyYW1lKG1ldGFkYXRhLCByb3cubmFtZXMgPSBtZXRhZGF0YSRgU2FtcGxlIE5hbWVgKSkKVFJFRSA9IHBoeV90cmVlKGZpdCR0cmVlKQpgYGAKCgoKRmlyc3QgY2hlY2sgdGhhdCB0aGUgaW5wdXRzIGFyZSBpbiBjb21wYXRpYmxlIGZvcm1hdHMgYnkgY2hlY2tpbmcgZm9yIEFTViBuYW1lcyB3aXRoIHRoZSBwaHlsb3NlcSBmdW5jdGlvbiwgdGF4YV9uYW1lcwpgYGB7cn0KaGVhZCh0YXhhX25hbWVzKFRBWCkpCmhlYWQodGF4YV9uYW1lcyhBU1YpKQpoZWFkKHRheGFfbmFtZXMoVFJFRSkpCmBgYAoKCkFuZCBjaGVjayBzYW1wbGUgbmFtZXMgd2VyZSBhbHNvIGRldGVjdGVkCmBgYHtyfQpoZWFkKHNhbXBsZV9uYW1lcyhBU1YpKQpoZWFkKHNhbXBsZV9uYW1lcyhNRVRBKSkKYGBgCgpBbmQgbWFrZSB0aGUgcGh5bG9zZXEgb2JqZWN0CmBgYHtyfQpwcyA8LSBwaHlsb3NlcShBU1YsCVRBWCwJTUVUQSAsIFRSRUUpCmBgYAoKCgpDaGVjayBzb21lIGZlYXR1cmVzIG9mIHRoZSBwaHlsb3NlcSBvYmplY3QKYGBge3J9CnJhbmtfbmFtZXMocHMpCnRhYmxlKHRheF90YWJsZShwcylbLCAiU3VwZXJncm91cCJdLCBleGNsdWRlID0gTlVMTCkKdW5pcXVlKHRheF90YWJsZShwcylbLCAiU3VwZXJncm91cCJdKQpgYGAKCgpGaWx0ZXIgb3V0IHRob3NlIGFtYmlnaW91cyBTdXBlcmdyb3VwIGFubm90YXRpb25zLSBsb3NpbmcgNDcxIEFTVnMKYGBge3J9CnBzIDwtIHN1YnNldF90YXhhKHBzLCAhaXMubmEoU3VwZXJncm91cCkgJiAhU3VwZXJncm91cCAlaW4lIGMoIiIsICJOQSIpKQp0YWJsZSh0YXhfdGFibGUocHMpWywgIlN1cGVyZ3JvdXAiXSwgZXhjbHVkZSA9IE5VTEwpCmBgYAoKQ2hlY2sgb3V0IHRoZSBEaXZpc2lvbiBuYW1lcwpgYGB7cn0KdGFibGUodGF4X3RhYmxlKHBzKVssICJEaXZpc2lvbiJdLCBleGNsdWRlID0gTlVMTCkKYGBgCgoKRmlsdGVyIG91dCBhbnkgd2l0aCAiTkEiIGFzIERpdmlzaW9uIApgYGB7cn0KcHMgPC0gc3Vic2V0X3RheGEocHMsICFpcy5uYShEaXZpc2lvbikgJiAhRGl2aXNpb24gJWluJSBjKCIiKSkKdGFibGUodGF4X3RhYmxlKHBzKVssICJEaXZpc2lvbiJdLCBleGNsdWRlID0gTlVMTCkKYGBgCkFmdGVyIHRoZSBhYm92ZSwgMTMsNDI3IEFTVnMgcmVtYWluIGZyb20gdGhlIG9yaWdpbmFsIDE0LDE3NwoKCkVsaW1pbmF0ZSB0aGUgbGlicmFyaWVzIHRoYXQgZGlkbid0IGhhdmUgbWFueSBzZXF1ZW5jZXMsIEFFM2ExOThBLCBBRTNiMzE0QSwgQUUyYTIwMEEsIEFFMmI5MDBBTiwgQUUyYTIwMEIsIEFFMmEyNjdCLCBBRTJhOTAwQk4KYGBge3J9CnRheGFfdG9fa2VlcCA8LSAhc2FtcGxlX25hbWVzKHBzKSAlaW4lIGMoIkFFM2ExOThBIiwiQUUzYjMxNEEiLCJBRTJhMjAwQSIsIkFFMmI5MDBBTiIsIkFFMmEyMDBCIiwiQUUyYTI2N0IiLCJBRTJhOTAwQk4iKQpwcyA8LSBwcnVuZV9zYW1wbGVzKHRheGFfdG9fa2VlcCwgcHMpCmBgYAo0MSBzYW1wbGVzIHJlbWFpbiBhbmQgc3RpbCAxMyw0MjcgQVNWcwoKCgpDaGVjayByYXJlZmFjdGlvbiBjdXJ2ZSBhZ2FpbiB0byBtYWtlIHN1cmUgdGhvc2UgbG93LXNxZXVlbmNpbmctZWZmb3J0IHNhbXBsZXMgaGF2ZSBiZWVuIHJlbW92ZWQKYGBge3J9CnJhcmVjdXJ2ZSh0KG90dV90YWJsZShwcykpLCBzdGVwPTEwMCwgY2V4PTAuNSwgeWxhYj0iQVNWcyIsIGxhYmVsPVQpCmBgYAoKCgojIyMgUmUtcm9vdCB0cmVlIApIYXZlIHRvIGRvIHRoaXMgYmVjYXVzZSB5b3UgbWF5IGhhdmUgcmVtb3ZlZCB0aGUgcm9vdCBvZiB5b3VyIHRyZWUgd2hlbiBwcnVuaW5nKS4gCihJIGZvdW5kIHRoaXMgaGFuZHkgZnVuY3Rpb24gZnJvbSBbaGVyZV0oaHR0cHM6Ly9qb2huLXF1ZW5zZW4uY29tL3IvdW5pZnJhYy1hbmQtdHJlZS1yb290cy8pIHdoaWNoIHBpY2tzIHRoZSBsb25nZXN0IGJyYW5jaCB0byByb290IGZyb20pLiAKCmBgYHtyfQojIGZpcnN0IGRlZmluZSBmdW5jdGlvbiBmcm9tIGxpbmsgYWJvdmUgdG8gZmluZCBmdXJ0aGVzdCBvdXRncm91cApwaWNrX25ld19vdXRncm91cCA8LSBmdW5jdGlvbih0cmVlLnVucm9vdGVkKXsKcmVxdWlyZSgibWFncml0dHIiKQpyZXF1aXJlKCJkYXRhLnRhYmxlIikKcmVxdWlyZSgiYXBlIikgIyBhcGU6Ok50aXAKIyB0YWJsaWZ5IHBhcnRzIG9mIHRyZWUgdGhhdCB3ZSBuZWVkLgp0cmVlRFQgPC0gCiAgICAgY2JpbmQoCiAgICAgICAgIGRhdGEudGFibGUodHJlZS51bnJvb3RlZCRlZGdlKSwKICAgICAgICAgZGF0YS50YWJsZShsZW5ndGggPSB0cmVlLnVucm9vdGVkJGVkZ2UubGVuZ3RoKQogICAgIClbMTpOdGlwKHRyZWUudW5yb290ZWQpXSAlPiUgCiBjYmluZChkYXRhLnRhYmxlKGlkID0gdHJlZS51bnJvb3RlZCR0aXAubGFiZWwpKQogIyBUYWtlIHRoZSBsb25nZXN0IHRlcm1pbmFsIGJyYW5jaCBhcyBvdXRncm91cAogbmV3Lm91dGdyb3VwIDwtIHRyZWVEVFt3aGljaC5tYXgobGVuZ3RoKV0kaWQKIHJldHVybihuZXcub3V0Z3JvdXApIH0KCiMgdGhlbiBydW4gb24gbXkgcGh5bG9zZXEgdHJlZQpteS50cmVlIDwtIHBoeV90cmVlKHBzKQpvdXQuZ3JvdXAgPC0gcGlja19uZXdfb3V0Z3JvdXAobXkudHJlZSkKb3V0Lmdyb3VwCgojIFRoZW4gdXNlIHRoaXMgb3V0Z3JvdXAgdG8gcm9vdCB0aGUgdHJlZQpuZXcudHJlZTEgPC0gYXBlOjpyb290KG15LnRyZWUsIG91dGdyb3VwPW91dC5ncm91cCwgcmVzb2x2ZS5yb290PVRSVUUpCgoKcGh5X3RyZWUocHMpIDwtIG5ldy50cmVlMQoKIyBDaGVjayBpZiB0cmVlIGlzIGJpbmFyeSAoZGljaG90b21vdXMgbm90IG11bHRpY2hvdG9tb3VzKQppcy5iaW5hcnkudHJlZShwaHlfdHJlZShwcykpCgojIElmIGZhbHNlLCB3b3VsZCBoYXZlIHRvIHJ1bgojIG5ldy50cmVlMiA8LSBhcGU6Om11bHRpMmRpKG5ldy50cmVlMSkKIyBwaHlfdHJlZShwcykgPC0gbmV3LnRyZWUyCiMgcGh5X3RyZWUocHMpCgoKYGBgCgojIyMgQ2hlY2sgcGh5bGEgdXNpbmcgYmFyIHBsb3RzCgpDaGVjayBvdmVyYWxsIGhvdyB0aGUgcGh5bGEgYXJlIGRpc3RyaWJ1dGVkIGFtb25nIHNhbXBsZXMuIFBoeWxvc2VxIG1ha2VzIHRoaXMgZWFzeQoKYGBge3J9CiMgRmlyc3QgYWdsb21lcmF0ZSB0aGUgQVNWcyBhdCB0aGUgcGh5bHVtIGxldmVsIHVzaW5nIHRoZSBwaHlsb3NlcSBmdW5jdGlvbiwgdGF4X2dsb20KRGl2aXNpb25HbG9tbWVkID0gdGF4X2dsb20ocHMsICJEaXZpc2lvbiIpCgojIFRoZXJlIGFyZSBtYW55IHBoeWxhIGhlcmUsIHNvIGhhdmUgdG8gbWFrZSBhIGN1c3RvbSBjb2xvciBwYWxldHRlIGJ5IGludGVycG9sYXRpbmcgZnJvbSBhbiBleGlzdGluZyBvbmUgaW4gUkNvbG9yQnJld2VyCmNvbG91ckNvdW50ID0gbGVuZ3RoKHRhYmxlKHRheF90YWJsZShwcylbLCAiRGl2aXNpb24iXSwgZXhjbHVkZSA9IE5VTEwpKQpnZXRQYWxldHRlID0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDExLCAiU3BlY3RyYWwiKSkKRGl2aXNpb25QYWxldHRlID0gZ2V0UGFsZXR0ZShjb2xvdXJDb3VudCkKCiMgYW5kIHBsb3QKcGxvdF9iYXIoRGl2aXNpb25HbG9tbWVkLCB4ID0gIlNhbXBsZSIsIGZpbGwgPSAiRGl2aXNpb24iKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IERpdmlzaW9uUGFsZXR0ZSkKYGBgCgoKClBsb3QgY29tcG9zaXRpb25hbCAocmVsYXRpdmUgYWJ1bmRhbmNlcykgaW5zdGVhZCBvZiBhYnNvbHV0ZSBhYnVuZGFuY2UgdXNpbmcgbWljcm9iaW9tZTo6dHJhbnNmb3JtCgpgYGB7cn0KcHNfcmEgPC0gbWljcm9iaW9tZTo6dHJhbnNmb3JtKHBzLCB0cmFuc2Zvcm0gPSAiY29tcG9zaXRpb25hbCIpCihvdHVfdGFibGUocHNfcmEpKVsxOjUsMTo1XQpgYGAKCgpgYGB7cn0KIyBUaGVuIGFnbG9tZXJhdGUgdGhlIEFTVnMgYXQgdGhlIHBoeWx1bSBsZXZlbCB1c2luZyB0aGUgcGh5bG9zZXEgZnVuY3Rpb24sIHRheF9nbG9tCkRpdmlzaW9uR2xvbW1lZF9SQSA9IHRheF9nbG9tKHBzX3JhLCAiRGl2aXNpb24iKQojIGFuZCBwbG90CkRpdmlzaW9uX2JhcnBsb3QgPC0gcGxvdF9iYXIoRGl2aXNpb25HbG9tbWVkX1JBLCB4ID0gIlNhbXBsZSIsIGZpbGwgPSAiRGl2aXNpb24iKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IERpdmlzaW9uUGFsZXR0ZSkgKwogIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCkpCiAgCgpEaXZpc2lvbl9iYXJwbG90CgojIGV4cG9ydApnZ3NhdmUoIkZpZ3VyZXMvRGl2aXNpb25fYmFycGxvdC5lcHMiLERpdmlzaW9uX2JhcnBsb3QsIHdpZHRoID0gMTUsIGhlaWdodCA9IDUsIHVuaXRzID0gYygiaW4iKSkKYGBgCgpMb3RzIG9mIGRpbm9mbGFnZWxsYXRlcyBhbmQgcmFkaW9sYXJpYS4gTWFrZXMgc2Vuc2UuIEJ1dCB0aGUgYWJvdmUgaXMgdGhlIGRpc3RyaWJ1dGlvbiBmcm9tIGFsbCBzYW1wbGVzLiBOZXh0IG1ha2UgcGxvdHMgdGhhdCBpbmRpY2F0ZSBkaXN0cmlidXRpb25zIGFjcm9zcyBlbnZpcm9ubWVudGFsIGdyYWRpZW50cy4gQ2FsY3VsYXRlIGF2ZXJhZ2VzIGFuZCB1c2UgYnViYmxlIHBsb3RzCgoKCgoKIyMjIENhbGN1bGF0ZSBhdmVyYWdlcyBmcm9tIHJlcGxpY2F0ZXMKR2V0IGF2ZXJhZ2UgcmVsYXRpdmUgYWJ1bmRhbmNlcyBmcm9tIHNhbXBsZSByZXBsaWNhdGVzCmBgYHtyfQoKb3R1X3RhYmxlX21lYW5fcmEgPC0gCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIxMDNBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhMTAzQSIsIkFFM2IxMDNBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjE5OEEiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2IxOThBIikpLCBuYS5ybSA9IFRSVUUpKSAgJT4lICMgU2FtcGxlIEFFM2ExOThBIHdhcyByZW1vdmVkCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyMzRBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhMjM0QSIsIkFFM2IyMzRBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjI5NUEiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2EyOTVBIiwiQUUzYjI5NUEiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMzE0QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUzYTMxNEEiKSksIG5hLnJtID0gVFJVRSkpICU+JSAgIyBTYW1wbGUgQUUzYjMxNEEgd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjkwMEFNIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhOTAwQU0iLCJBRTFiOTAwQU0iKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMTAzQiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUzYTEwM0IiLCJBRTNiMTAzQiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIxOThCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhMTk4QiIsIkFFM2IxOThCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjIzNEIiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFM2EyMzRCIiwiQUUzYjIzNEIiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMjk1QiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUzYTI5NUIiLCJBRTNiMjk1QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIzMTRCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhMzE0QiIsIkFFM2IzMTRCIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjkwMEJNIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTNhOTAwQk0iLCJBRTFiOTAwQk0iKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMTQzQSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUyYTE0M0EiLCJBRTJiMTQzQSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyMDBBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJiMjAwQSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYTIwMEEgd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjIzN0EiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmEyMzdBIiwiQUUyYjIzN0EiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMjQ3QSIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUyYTI0N0EiLCJBRTJiMjQ3QSIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyNjdBIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJhMjY3QSIsIkFFMmIyNjdBIikpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjkwMEFOIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJhOTAwQU4iKSksIG5hLnJtID0gVFJVRSkpICU+JSAjIEFFMmI5MDBBTiB3YXMgcmVtb3ZlZAogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMTQzQiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUyYTE0M0IiLCJBRTJiMTQzQiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyMDBCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJiMjAwQiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYTIwMEIgd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjIzN0IiID0gcm93TWVhbnMoc2VsZWN0KGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksIGMoIkFFMmEyMzdCIiwiQUUyYjIzN0IiKSksIG5hLnJtID0gVFJVRSkpICU+JQogIG11dGF0ZShkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCAiMjQ3QiIgPSByb3dNZWFucyhzZWxlY3QoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgYygiQUUyYTI0N0IiLCJBRTJiMjQ3QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX3JhKSksICIyNjdCIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJiMjY3QiIpKSwgbmEucm0gPSBUUlVFKSkgJT4lICMgQUUyYTI2N0Igd2FzIHJlbW92ZWQKICBtdXRhdGUoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfcmEpKSwgIjkwMEJOIiA9IHJvd01lYW5zKHNlbGVjdChkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYSkpLCBjKCJBRTJiOTAwQk4iKSksIG5hLnJtID0gVFJVRSkpICMgQUUyYTkwMEJOIHdhcyByZW1vdmVkCgpvdHVfdGFibGVfbWVhbl9yYSA8LSBvdHVfdGFibGVfbWVhbl9yYVssdW5pcXVlKG1ldGFkYXRhJFJlcGxpY2F0ZSldCgpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCk1ha2UgaW50byBuZXcgcGh5bG9zZXEgb2JqZWN0CmBgYHtyfQptZXRhZGF0YTIgPC0gdW5pcXVlKHNlbGVjdChtZXRhZGF0YSwhYygnU2FtcGxlIE5hbWUnLFR5cGUsY29sbmFtZXMoU1JBUnVuVGFibGUpKSkpCk1FVEEyIDwtIHNhbXBsZV9kYXRhKGRhdGEuZnJhbWUobWV0YWRhdGEyLCByb3cubmFtZXMgPSBtZXRhZGF0YTIkUmVwbGljYXRlKSkKCnBzX3JhX21lYW4gPC0gcGh5bG9zZXEob3R1X3RhYmxlKG90dV90YWJsZV9tZWFuX3JhLCB0YXhhX2FyZV9yb3dzID0gVFJVRSksIFRBWCwgVFJFRSwgTUVUQTIpCmBgYAoKCmBgYHtyfQojIEZpcnN0IGFnbG9tZXJhdGUgdGhlIEFTVnMgYXQgdGhlIHBoeWx1bSBsZXZlbCB1c2luZyB0aGUgcGh5bG9zZXEgZnVuY3Rpb24sIHRheF9nbG9tCnBzX3JhX21lYW5fZGl2aXNpb24gPC0gdGF4X2dsb20ocHNfcmFfbWVhbiwgIkRpdmlzaW9uIikKCgojIGFuZCBjaGVjayBieSBiYXIgcGxvdHRpbmcKcGxvdF9iYXIocHNfcmFfbWVhbl9kaXZpc2lvbiwgeCA9ICJTYW1wbGUiLCBmaWxsID0gIkRpdmlzaW9uIikgKyAKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBEaXZpc2lvblBhbGV0dGUpCmBgYAoKIyBBYnVuZGFuY2UgUGxvdHMKCiMjIFByZXBhcmUgZGF0YQoKRXh0cmFjdCBtZWFuIHJlbGF0aXZlIGFidW5kYW5jZSwgZ2xvbW1lZCBieSBkaXZpc2lvbiwgZnJvbSB0aGUgcGh5bG9zZXEgb2JqZWN0IGFuZCBwYWlyIGl0IHRvIHRheG9ub21pYyBkYXRhCmBgYHtyfQpkaXZpc2lvbl9kZiA8LSBkYXRhLmZyYW1lKG90dV90YWJsZShwc19yYV9tZWFuX2RpdmlzaW9uKSkKY29sbmFtZXMoZGl2aXNpb25fZGYpIDwtIGNvbG5hbWVzKG90dV90YWJsZShwc19yYV9tZWFuX2RpdmlzaW9uKSkKZGl2aXNpb25fZGYkQVNWIDwtIHJvd25hbWVzKGRpdmlzaW9uX2RmKQoKb3R1X3RhYmxlX21lYW5fcmEgPC0gbGVmdF9qb2luKGRpdmlzaW9uX2RmLCBhc190aWJibGUodGF4b25vbXksIHJvd25hbWVzID0gIkFTViIpLCBieSA9ICJBU1YiKQpvdHVfdGFibGVfbWVhbl9yYQpgYGAKCgpQaXZvdCBsb25nZXIKYGBge3J9Cm90dV90YWJsZV9tZWFuX3JhIDwtIHBpdm90X2xvbmdlcihvdHVfdGFibGVfbWVhbl9yYSwgY29scyA9IHVuaXF1ZShtZXRhZGF0YSRSZXBsaWNhdGUpLCBuYW1lc190byA9ICJSZXBsaWNhdGUiLCB2YWx1ZXNfdG8gPSAiTWVhbl9SQSIpCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKCkpvaW4gbWV0YWRhdGEgCmBgYHtyfQpvdHVfdGFibGVfbWVhbl9yYSA8LSBsZWZ0X2pvaW4ob3R1X3RhYmxlX21lYW5fcmEsIHVuaXF1ZShzZWxlY3QobWV0YWRhdGEsIGMoIlJlcGxpY2F0ZSIsICJEZXB0aCIsICJTaXplRnJhY3Rpb24iLCAiU2Vhc29uIiwgIk94Q29uZCIsICJGbHVvcmVzY2VuY2UiLCAiQmVhbUF0dCIsICJPMiIsICJUZW1wIiwgIlNhbGluaXR5IiwgIkgyUyIsICJQYXJ0aWN1bGF0ZVMiLCAiVFpWUyIsICJDSDQiLCAiTk8zIiwgIk5PMiIsICJOSDQiLCAiUE80IiwgIkNoZW1vYXV0b3Ryb3BoeSIsICJCTlAiLCAiTWljcm9BYnVuKHgxMF44IExeLTEpIiwgIkZsYWdBYnVuKHgxMF41IEwtMSkiLCAiVkxQKHgxMF44IEwtMSkiKSkpLCBieSA9ICJSZXBsaWNhdGUiKQoKIyBSZXBsYWNlIHplcm9lcyBpbiBSQSB3aXRoIE5BIChiZXR0ZXIgZm9yIHBsb3R0aW5nKQpvdHVfdGFibGVfbWVhbl9yYSRNZWFuX1JBW290dV90YWJsZV9tZWFuX3JhJE1lYW5fUkEgPT0gMF0gPC0gTkEKCm90dV90YWJsZV9tZWFuX3JhCmBgYAoKCiMjIEJ1YmJsZSBQbG90IG9mIERpdmlzaW9ucwoKYGBge3J9CiMgcmVvcmRlciBzb21lIGZhY3RvcnMgdG8gbWFrZSB0aGVtIHBsb3QgaW4gdGhlIG9yZGVyIEkgd2FudApvdHVfdGFibGVfbWVhbl9yYSRPeENvbmQgPC0gZmFjdG9yKG90dV90YWJsZV9tZWFuX3JhJE94Q29uZCwgbGV2ZWxzID0gYygiT3h5Y2xpbmUiLCAiU2hhbGxvd0Fub3hpYyIsICJFdXhpbmljIikpCm90dV90YWJsZV9tZWFuX3JhJFNpemVGcmFjdGlvbiA8LSBmYWN0b3Iob3R1X3RhYmxlX21lYW5fcmEkU2l6ZUZyYWN0aW9uLCBsZXZlbHMgPSBjKCJQQSIsICJGTCIpKQoKZXVrX2RpdmlzaW9uc19idWJibGVwbG90X2NvbG9yIDwtIGdncGxvdChvdHVfdGFibGVfbWVhbl9yYSxhZXMgKHggPSBhcy5jaGFyYWN0ZXIoRGVwdGgpLCB5ID0gcmVvcmRlcihEaXZpc2lvbiwgTWVhbl9SQSwgZnVuY3Rpb24oeCl7c3VtKHgsbmEucm0gPSBUUlVFKX0pLCBjb2xvciA9IE94Q29uZCkpICsgCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9TWVhbl9SQSkpKwogIGZhY2V0X3dyYXAoU2Vhc29uflNpemVGcmFjdGlvbiwgc2NhbGVzID0gImZyZWVfeCIsIGRyb3A9IFRSVUUsIG5jb2wgPSA0KSArCiAgc2NhbGVfc2l6ZShyYW5nZSA9IGMoMSwxNSkpICsKICBzY2FsZV9zaXplX2FyZWEoYnJlYWtzID0gYygwLC4yNSwuNSwuNzUsMSksIG1heF9zaXplID0gNikgKwogIHhsYWIoIkRlcHRoIikgKwogIHlsYWIoIiIpICsKICBsYWJzKHNpemU9IlJlbGF0aXZlIEFidW5kYW5jZSIsIGNvbG9yID0gIlJlZG94IENvbmRpdGlvbiIpICsKICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIiwgImJyb3duNCIpKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCksCiAgICAgICAgYXhpcy50aXRsZS54PSBlbGVtZW50X3RleHQoc2l6ZT0xMiksCiAgICAgICAgYXhpcy50aXRsZS55PSBlbGVtZW50X3RleHQoc2l6ZT0xMikpCgpldWtfZGl2aXNpb25zX2J1YmJsZXBsb3RfY29sb3IKYGBgCgpTYXZlIGZpZ3VyZQpgYGB7cn0KZ2dzYXZlKGZpbGVuYW1lID0gIkZpZ3VyZXMvZXVrX2RpdmlzaW9uc19idWJibGVwbG90X2NvbG9yLmVwcyIsIHBsb3QgPSBldWtfZGl2aXNpb25zX2J1YmJsZXBsb3RfY29sb3IsIHVuaXRzID0gYygiaW4iKSwgd2lkdGggPSAxMCwgaGVpZ2h0ID0gNiwgZHBpID0gMzAwKQpgYGAKCgojIyMjIFNhdmUgYW5kIHJlLWxvYWQgZGF0YXNldApgYGB7cn0KIyBzYXZlLmltYWdlKCJFbnZpcm9ubWVudEJhY2t1cHMvQ2FyaWFjb0V1a3NfcG9zdGFuYWx5c2lzX3ZhcnNfdXB0b19idWJibGVwbG90cy5SRGF0YSIpCmBgYAoKUmUtbG9hZApgYGB7cn0KbG9hZCgiRW52aXJvbm1lbnRCYWNrdXBzL0NhcmlhY29FdWtzX3Bvc3RhbmFseXNpc192YXJzX3VwdG9fYnViYmxlcGxvdHMuUkRhdGEiKQpgYGAKCgojIENvcnJlbGF0aW9uIEFuYWx5c2VzCgojIyBQcmVwYXJlIHRoZSBkYXRhCgojIyMgSW1wb3J0IHByb2thcnlvdGUgZGF0YXNldCBmcm9tIFN1dGVyIGV0IGFsLiAyMDE4CgpJbXBvcnQKYGBge3J9CmFyY2hfY291bnRzIDwtIHJlYWRfY3N2KCJTdXRlcl8yMDE4X2NvdW50X3RhYmxlcy9DYXJpYWNvX0FBX3VwZGF0ZWRfcmF3LmNzdiIpOwpiYWNfY291bnRzIDwtIHJlYWRfY3N2KCJTdXRlcl8yMDE4X2NvdW50X3RhYmxlcy9DYXJpYWNvX0FCX3VwZGF0ZWRfcmF3LmNzdiIpOwpgYGAKCkdldCBzYW1wbGUgbmFtZXMKYGBge3J9CmJhY19zYW1wbGVzIDwtIGNvbG5hbWVzKGJhY19jb3VudHMpWzI6NDldCmFyY2hfc2FtcGxlcyA8LSBjb2xuYW1lcyhhcmNoX2NvdW50cylbMjo0N10KCmJhY19zYW1wbGVzCmFyY2hfc2FtcGxlcwpgYGAKCk1ha2Ugc2VwYXJhdGUgdGF4b25vbXkgYW5kIGNvdW50IHZhcmlhYmxlcwpgYGB7cn0KYXJjaF9PVFUgPC0gYXJjaF9jb3VudHNbLGMoIiNPVFUgSUQiLGFyY2hfc2FtcGxlcyldCmFyY2hfdGF4b25vbXkgPC0gIGFyY2hfY291bnRzICU+JQogIHNlbGVjdCgtYXJjaF9zYW1wbGVzKSAgJT4lCiAgc2VsZWN0KC1TdW0pCgphcmNoX09UVQphcmNoX3RheG9ub215CgpiYWNfT1RVIDwtIGJhY19jb3VudHNbLGMoIiNPVFUgSUQiLGJhY19zYW1wbGVzKV0KYmFjX3RheG9ub215IDwtICBiYWNfY291bnRzICU+JQogIHNlbGVjdCgtYmFjX3NhbXBsZXMpICAlPiUKICBzZWxlY3QoLVN1bSkgJT4lCiAgc2VsZWN0KC0iSW50ZXJlc3RpbmcgY2xvc2UgcmVsYXRpdmVzIikKCmJhY19PVFUKYmFjX3RheG9ub215CmBgYAoKCiMjIyBNYWtlIGludG8gcGh5bG9zZXEgb2JqZWN0cwpgYGB7cn0KYmFjX09UVSA8LSB0eXBlX2NvbnZlcnQoYXMuZGF0YS5mcmFtZShiYWNfT1RVKSkKcm93bmFtZXMoYmFjX09UVSkgPC0gYmFjX09UVSRgI09UVSBJRGAKYmFjX09UVSA8LSBiYWNfT1RVWywhbmFtZXMoYmFjX09UVSkgJWluJSAoYygiI09UVSBJRCIpKV0KCmJhY19PVFUJPQlvdHVfdGFibGUoYmFjX09UVSwgdGF4YV9hcmVfcm93cyA9ICBUUlVFKQojCmFyY2hfT1RVIDwtIHR5cGVfY29udmVydChhcy5kYXRhLmZyYW1lKGFyY2hfT1RVKSkKcm93bmFtZXMoYXJjaF9PVFUpIDwtIGFyY2hfT1RVJGAjT1RVIElEYAphcmNoX09UVSA8LSBhcmNoX09UVVssIW5hbWVzKGFyY2hfT1RVKSAlaW4lIChjKCIjT1RVIElEIikpXQoKYXJjaF9PVFUJPQlvdHVfdGFibGUoYXJjaF9PVFUsIHRheGFfYXJlX3Jvd3MgPSAgVFJVRSkKIwpiYWNfVEFYIDwtIHR5cGVfY29udmVydChhcy5kYXRhLmZyYW1lKGJhY190YXhvbm9teSkpCnJvd25hbWVzKGJhY19UQVgpIDwtIGJhY19UQVgkYCNPVFUgSURgCmJhY19UQVggPC0gYmFjX1RBWFssIW5hbWVzKGJhY19UQVgpICVpbiUgKGMoIiNPVFUgSUQiKSldCgpiYWNfVEFYCT0JdGF4X3RhYmxlKGFzLm1hdHJpeChiYWNfVEFYKSkKIwphcmNoX1RBWCA8LSB0eXBlX2NvbnZlcnQoYXMuZGF0YS5mcmFtZShhcmNoX3RheG9ub215KSkKcm93bmFtZXMoYXJjaF9UQVgpIDwtIGFyY2hfVEFYJGAjT1RVIElEYAphcmNoX1RBWCA8LSBhcmNoX1RBWFssIW5hbWVzKGFyY2hfVEFYKSAlaW4lIChjKCIjT1RVIElEIikpXQoKYXJjaF9UQVgJPQl0YXhfdGFibGUoYXMubWF0cml4KGFyY2hfVEFYKSkKIwpNRVRBCT0Jc2FtcGxlX2RhdGEoZGF0YS5mcmFtZShtZXRhZGF0YSwgcm93Lm5hbWVzID0gbWV0YWRhdGEkYFNhbXBsZSBOYW1lYCkpCiMKCnBzX2JhYyA8LSBwaHlsb3NlcShiYWNfT1RVLAliYWNfVEFYLAlNRVRBKQpwc19hcmNoIDwtIHBoeWxvc2VxKGFyY2hfT1RVLAlhcmNoX1RBWCwJTUVUQSkKYGBgCgpGaWx0ZXIgb3V0IHRoZSBzYW1wbGVzIHdpdGggbG93IHNlcXVlbmNpbmcgZWZmb3J0LiBUaGVzZSB3ZXJlIHByZXZpb3VzbHkgaWRlbnRpZmllZCBmb3IgaXRhZ3MgcGFwZXIKCmBgYHtyfQp0YXhhX3RvX2tlZXBfYiA8LSAhc2FtcGxlX25hbWVzKHBzX2JhYykgJWluJSBjKCJBQjNhOTAwQSIsIkFCMmEyMDBBIiwiQUIyYjI2N0EiKQpwc19iYWMgPC0gcHJ1bmVfc2FtcGxlcyh0YXhhX3RvX2tlZXBfYiwgcHNfYmFjKQoKdGF4YV90b19rZWVwX2EgPC0gIXNhbXBsZV9uYW1lcyhwc19hcmNoKSAlaW4lIGMoIkFBMmI5MDBBTiIsIkFBMmEyNDdCIiwiQUEyYTkwMEJOIiwiQUEyYjkwMEJOIikKcHNfYXJjaCA8LSBwcnVuZV9zYW1wbGVzKHRheGFfdG9fa2VlcF9hLCBwc19hcmNoKQpgYGAKCgojIyMgRmlsdGVyaW5nCgpGaXJzdCBjYWxjdWxhdGUgcmVsYXRpdmUgYWJkdW5hbmNlIG9mIGJhYyBhbmQgYXJjaCBPVFUgdGFibGVzCmBgYHtyfQpwc19iYWNfcmEgPC0gbWljcm9iaW9tZTo6dHJhbnNmb3JtKHBzX2JhYywgdHJhbnNmb3JtID0gImNvbXBvc2l0aW9uYWwiKQoob3R1X3RhYmxlKHBzX2JhY19yYSkpWzE6NSwxOjVdCgpwc19hcmNoX3JhIDwtIG1pY3JvYmlvbWU6OnRyYW5zZm9ybShwc19hcmNoLCB0cmFuc2Zvcm0gPSAiY29tcG9zaXRpb25hbCIpCihvdHVfdGFibGUocHNfYXJjaF9yYSkpWzE6NSwxOjVdCmBgYAoKCiMjIyBGaWx0ZXIgbG93IGFidW5kYW5jZSBzcGVjaWVzIGZyb20gZnVsbCBkYXRhc2V0CgpSZW1vdmUgcm93cyBvZiBnbG9tbWVkIHRheGEgZnJvbSB0aGUgZnVsbCBkYXRhZnJhbWUgaWYgdGhlaXIgc3VtIGFjcm9zcyBhbGwgc2FtcGxlcyBkb2Vzbid0IGV4Y2VlZCA1JSAoUkEgPiAwLjA1KSAKYGBge3J9CiMgQmFjdGVyaWEKeCA8LSB0YXhhX3N1bXMocHNfYmFjX3JhKQojIGtlZXBUYXhhIDwtICBiYXNlOjp3aGljaCh4ICA+IC4wNSkKa2VlcFRheGEgPC0gIHg+LjA1ICMgcHJ1bmVfdGF4YSByZXF1aXJlIGEgbG9naWNhbCBub3QgYSBsaXN0IG9mIElEcy4gY29tcGFyZSB0byBrZWVwVGF4YSBhYm92ZSB0byBjaGVjawpwc19iYWNfcmFfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19iYWNfcmEpCnBzX2JhY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2JhYykKcHNfYmFjX3JhX3BydW5lZApwc19iYWNfcHJ1bmVkCgojIEFyY2hhZWEKeCA8LSB0YXhhX3N1bXMocHNfYXJjaF9yYSkKIyBrZWVwVGF4YSA8LSAgYmFzZTo6d2hpY2goeCAgPiAuMDUpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfYXJjaF9yYV9wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2FyY2hfcmEpCnBzX2FyY2hfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19hcmNoKQpwc19hcmNoX3JhX3BydW5lZApwc19hcmNoX3BydW5lZAoKIyBFdWthcnlvdGVzCnggPC0gdGF4YV9zdW1zKHBzX3JhKQojIGtlZXBUYXhhIDwtICBiYXNlOjp3aGljaCh4ICA+IC4wNSkKa2VlcFRheGEgPC0gIHg+LjA1ICMgcHJ1bmVfdGF4YSByZXF1aXJlIGEgbG9naWNhbCBub3QgYSBsaXN0IG9mIElEcy4gY29tcGFyZSB0byBrZWVwVGF4YSBhYm92ZSB0byBjaGVjawpwc19ldWtfcmFfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19yYSkKcHNfZXVrX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHMpCnBzX2V1a19yYV9wcnVuZWQKcHNfZXVrX3BydW5lZApgYGAKVHJpbW1lZCB0byAxMjQgYmFjdGVyaWEgT1RVcywgNTIgYXJjaGFlYSBPVFVzLCBhbmQgMTIzIGV1a2FyeW90aWMgQVNWcyAoMjk5IHRvdGFsKS4gUHJvY2VlZCB3aXRoIHRoaXMgZGF0YXNldCBvZiB0aGUgbW9zdCBhYnVuZGFudCBPVFVzIGZvciBjb3JyZWxhdGlvbnMgYW5kIG5ldHdvcmsgYW5hbHlzZXMuLi4KClRvIGRvIHRoZSBtdWx0aS1kb21haW4gYW5hbHlzaXMsIHRoZSBzYW1wbGUgbmFtZXMgZnJvbSBlYWNoIHBoeWxvc2VxIG9iamVjdCBtdXN0IG1hdGNoLiBUaGVzZSBjdXJyZW50bHkgaGF2ZSAiQiIgZm9yIGJhY3RlcmlhLCBBLCBFIGV0Yy4gUmVtb3ZlIHRoaXMgbGV0dGVyIGZyb20gc2FtcGxlIG5hbWVzIHNvIHRoYXQgIkFFMmEyNDdCIiwgIkFBMmEyNDdCIiwgIkFCMmEyNDdCIiBhbGwgYmVjb21lIGp1c3QgIlR5cGUiIGZyb20gdGhlIG1ldGFkYXRhIHNoZWV0IFtJbnROb3YxRkwgaW4gdGhpcyBjYXNlLSBmb3IgSW50ZXJmYWNlLCBOb3ZlbWJlciwgcmVwIDEsIGZyZWUtbGl2aW5nXS4gCgpJbXBvcnQgbXkgU2FtcGxlS2V5CmBgYHtyfQpzYW1wbGVrZXkgPC0gcmVhZF9jc3YoIlNhbXBsZUtleS5jc3YiKQpgYGAKCgpDaGFuZ2UgdGhlIHNhbXBsZSBuYW1lcyBpbiB0aGUgb3R1IHRhYmxlcyB0byBzYW1wbGUgIlR5cGUiCmBgYHtyfQojIEFyY2hhZWEKIyByZW1vdmUgbWlzc2luZyBhcmNoYWVhIHNhbXBsZXMgZnJvbSBzYW1wbGVrZXlfQQpzYW1wbGVrZXlfQSA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9hcmNoICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19hcmNoX3JhX3BydW5lZCkpKQojIHNvcnQgU2FtcGxlS2V5IGJ5IG9yZGVyIG9mIGNvbHVtbiBuYW1lcyBmcm9tIHBzX2FyY2hfcmFfcHJ1bmVkCnNhbXBsZWtleV9BIDwtIHNhbXBsZWtleV9BICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRF9hcmNoLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYXJjaF9yYV9wcnVuZWQpKSkpCiMgcmVwbGFjZSBjb2wgbmFtZXMgb2Ygb3R1IHRhYmxlIGZyb20gcHNfYXJjaF9yYV9wcnVuZWQKc2FtcGxlX25hbWVzKHBzX2FyY2hfcmFfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQSRUeXBlCiMgYW5kIHBzX2FyY2hfcHJ1bmVkCnNhbXBsZV9uYW1lcyhwc19hcmNoX3BydW5lZCkgPC0gc2FtcGxla2V5X0EkVHlwZQoKCiMgQmFjdGVyaWEKc2FtcGxla2V5X0IgPC0gZmlsdGVyKHNhbXBsZWtleSwgU2FtcGxlSURfYmFjICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19iYWNfcmFfcHJ1bmVkKSkpCnNhbXBsZWtleV9CIDwtIHNhbXBsZWtleV9CICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRF9iYWMsIGxldmVscyA9IGNvbG5hbWVzKG90dV90YWJsZShwc19iYWNfcmFfcHJ1bmVkKSkpKQpzYW1wbGVfbmFtZXMocHNfYmFjX3JhX3BydW5lZCkgPC0gc2FtcGxla2V5X0IkVHlwZQpzYW1wbGVfbmFtZXMocHNfYmFjX3BydW5lZCkgPC0gc2FtcGxla2V5X0IkVHlwZQoKCiMgRXVrYXJ5b3RlcwpzYW1wbGVrZXlfRSA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9ldWsgICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2V1a19yYV9wcnVuZWQpKSkKc2FtcGxla2V5X0UgPC0gc2FtcGxla2V5X0UgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2V1aywgbGV2ZWxzID0gY29sbmFtZXMob3R1X3RhYmxlKHBzX2V1a19yYV9wcnVuZWQpKSkpCnNhbXBsZV9uYW1lcyhwc19ldWtfcmFfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfRSRUeXBlCnNhbXBsZV9uYW1lcyhwc19ldWtfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfRSRUeXBlCgoKYGBgCgoKTW92ZSBhbGwgcHJ1bmVkIG90dSB0YWJsZXMgaW50byBvbmUgdGFibGUgYnkgbWF0Y2hpbmcgdGhlIHNhbXBsZSBUeXBlLSB3aWxsIHVzZSB0aGlzIGZvciBTcGFyQ0MKYGBge3J9CmFsbGRvbWFpbnNfZGYgPC0gYmluZF9yb3dzKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2JhY19wcnVuZWQpKSwgZGF0YS5mcmFtZShvdHVfdGFibGUocHNfYXJjaF9wcnVuZWQpKSwgZGF0YS5mcmFtZShvdHVfdGFibGUocHNfZXVrX3BydW5lZCkpKQphbGxkb21haW5zX2RmCmBgYAoKQ2hhbmdlIHJvdyBuYW1lcyBmcm9tICJkZW5vdm9YWFgiIHRvIG1lYW5pbmdmdWwgbmFtZXMKYGBge3J9CmFsbGRvbWFpbnNfZGZfZnVsbCA8LSBjYmluZChJRCA9IHJvd25hbWVzKGFsbGRvbWFpbnNfZGYpLCBhbGxkb21haW5zX2RmKQoKIyBzdGFydCB3aXRoIG9ubHkgZmlyc3Qgcm93cywgd2hpY2ggYXJlIGJhY3RlcmlhLiBtYWtlIG9uZSBjb2x1bW4gb2YgbWVhbmluZ2Z1bCBsYWJlbHMKdGVtcDEgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbFsxOmRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZCkpWzFdLF0sIGJhY190YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI09UVSBJRCIpKSAKdGVtcDEkTmV3X0lEIDwtIHBhc3RlKHRlbXAxJElELCB0ZW1wMSQidGF4b25vbXktMiIsIHRlbXAxJCJ0YXhvbm9teS0zIiwgdGVtcDEkInRheG9ub215LTQiKQp0ZW1wMSA8LSBzZWxlY3QodGVtcDEsLWNvbG5hbWVzKGJhY190YXhvbm9teVssMjoxMV0pKQoKIyBuZXh0IHJvd3MgYXJlIHRoZSBhcmNoYWVhIAp0ZW1wMiA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsW3N1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19wcnVuZWQpKVsxXSwxKTpzdW0oZGltKG90dV90YWJsZShwc19iYWNfcHJ1bmVkKSlbMV0sZGltKG90dV90YWJsZShwc19hcmNoX3BydW5lZCkpWzFdKSxdLCBhcmNoX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjT1RVIElEIikpIAp0ZW1wMiROZXdfSUQgPC0gcGFzdGUodGVtcDIkSUQsIHRlbXAyJCJ0YXhvbm9teS0yIiwgdGVtcDIkInRheG9ub215LTMiKQp0ZW1wMiA8LSBzZWxlY3QodGVtcDIsLWNvbG5hbWVzKGFyY2hfdGF4b25vbXlbLDI6OV0pKQoKCiMgbGFzdCByb3dzIGFyZSBldWthcnlhCmV1a190YXhvbm9teSA8LSBjYmluZCgiI0FTViBJRCIgPSByb3duYW1lcyh0YXhvbm9teSksIHRheG9ub215KQp0ZW1wMyA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsW3N1bShkaW0ob3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkKSlbMV0sIGRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZCkpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkKSlbMV0sIGRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZCkpWzFdLGRpbShvdHVfdGFibGUocHNfZXVrX3BydW5lZCkpWzFdKSxdLCBldWtfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNBU1YgSUQiKSkgCnRlbXAzJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMyRJRCwgdGVtcDMkIlN1cGVyZ3JvdXAiLCB0ZW1wMyQiRGl2aXNpb24iLCB0ZW1wMyQiQ2xhc3MiLCB0ZW1wMyQiT3JkZXIiKQp0ZW1wMyA8LSBzZWxlY3QodGVtcDMsLWNvbG5hbWVzKGV1a190YXhvbm9teVssMjo5XSkpCgojIGNvbWJpbmUgYmFjayBhbGwgMyBkb21haW5zLCB3aXRoIG5ldyBuYW1lcyBhcyByb3cgbmFtZXMgaW4gYSBkYXRhZnJhbWUKYWxsZG9tYWluc19kZl9mdWxsIDwtIHJiaW5kKHRlbXAxLCB0ZW1wMiwgdGVtcDMpCmFsbGRvbWFpbnNfZGZfZnVsbCA8LSBkYXRhLmZyYW1lKGFsbGRvbWFpbnNfZGZfZnVsbCkKcm93bmFtZXMoYWxsZG9tYWluc19kZl9mdWxsKSA8LSBhbGxkb21haW5zX2RmX2Z1bGwkTmV3X0lECmFsbGRvbWFpbnNfZGZfZnVsbCA8LSBzZWxlY3QoYWxsZG9tYWluc19kZl9mdWxsLCAtYygiSUQiLCJOZXdfSUQiKSkKCmBgYAoKCgpSZW1vdmUgY29sdW1ucyB3aXRoIE5Bcy4gVGhlc2UgYXJlIHNhbXBsZXMgZm9yIHdoaWNoIHRoZSBsaWJyYXJ5IGZvciBhdCBsZWFzdCBvbmUgZG9tYWluIGRpZG4ndCB3b3JrIChjYW4ndCBkbyBjb3JyZWxhdGlvbnMgd2l0aCBtaXNzaW5nIHZhbHVlcyBpbiBjb2x1bW5zKQpgYGB7cn0KYWxsZG9tYWluc19kZl9mdWxsIDwtIGFsbGRvbWFpbnNfZGZfZnVsbCAlPiUKICAgIHNlbGVjdF9pZih+ICFhbnkoaXMubmEoLikpKQp0KGFsbGRvbWFpbnNfZGZfZnVsbClbMTo1LDE6NV0KCmFsbGRvbWFpbnNfZGYgPC0gYWxsZG9tYWluc19kZiAlPiUKICAgIHNlbGVjdF9pZih+ICFhbnkoaXMubmEoLikpKQp0KGFsbGRvbWFpbnNfZGYpWzE6NSwxOjVdCmBgYAozNiBzYW1wbGVzIHJlbWFpbiBmb3IgY29ycmVsYXRpb24KCgojIyMgRmlsdGVyIGxvdyBhYnVuZGFuY2Ugc3BlY2llcy0gb3h5Y2xpbmUgZGVwdGhzIG9ubHkKU2ltbGFybHksIG1ha2UgcHJ1bmVkIGRhdGFzZXRzIG9mIHRoZSBtb3N0IGFidW5kYW50IE9UVXMvQVNWcyBpbiB0aGUgb3h5Y2xpbmUsIGFub3hpYywgYW5kIGV1eGluaWMgc2FtcGxlcyBhcyBzZXBhcmF0ZSBkYXRhc2V0cwoKUHVsbCBvdXQgc2FtcGxlcyBhbmQgdGF4YSBmcm9tIGVhY2ggcmVkb3ggcmVnaW1lCmBgYHtyfQojIFB1bGwgb3V0IG94eWNsaW5lIGJhY3RlcmlhIHNhbXBsZSBJRHMKb3h5Y2xpbmV0eXBlc19iYWMgPC0gbWV0YWRhdGEgJT4lIAogIGZpbHRlcihgU2FtcGxlIE5hbWVgICVpbiUgc2FtcGxlX25hbWVzKHBzX2JhYykpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIk94eWNsaW5lIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQpveHljbGluZXR5cGVzX2JhYyA8LSB1bmxpc3QoYyh1bmlxdWUob3h5Y2xpbmV0eXBlc19iYWMpKSwgdXNlLm5hbWVzID0gRkFMU0UpCgojIFB1bGwgb3V0IGFsbCBiYWN0ZXJpYSBmcm9tIG94eWNsaW5lCnBzX2JhY19veHljbGluZSA8LSAgcHJ1bmVfc2FtcGxlcyhveHljbGluZXR5cGVzX2JhYywgcHNfYmFjKQpwc19iYWNfcmFfb3h5Y2xpbmUgPC0gIHBydW5lX3NhbXBsZXMob3h5Y2xpbmV0eXBlc19iYWMsIHBzX2JhY19yYSkKCgojIFB1bGwgb3V0IG94eWNsaW5lIGFyY2hhZWEgc2FtcGxlIElEcwpveHljbGluZXR5cGVzX2FyY2ggPC0gbWV0YWRhdGEgJT4lIAogIGZpbHRlcihgU2FtcGxlIE5hbWVgICVpbiUgc2FtcGxlX25hbWVzKHBzX2FyY2gpKSAlPiUKICBmaWx0ZXIoT3hDb25kID09ICJPeHljbGluZSIpICU+JSAKICBzZWxlY3QoIlNhbXBsZSBOYW1lIikKb3h5Y2xpbmV0eXBlc19hcmNoIDwtIHVubGlzdChjKHVuaXF1ZShveHljbGluZXR5cGVzX2FyY2gpKSwgdXNlLm5hbWVzID0gRkFMU0UpCgojIFB1bGwgb3V0IGFsbCBhcmNoYWVhIGZyb20gb3h5Y2xpbmUKcHNfYXJjaF9veHljbGluZSA8LSAgcHJ1bmVfc2FtcGxlcyhveHljbGluZXR5cGVzX2FyY2gsIHBzX2FyY2gpCnBzX2FyY2hfcmFfb3h5Y2xpbmUgPC0gIHBydW5lX3NhbXBsZXMob3h5Y2xpbmV0eXBlc19hcmNoLCBwc19hcmNoX3JhKQoKCiMgUHVsbCBvdXQgb3h5Y2xpbmUgZXVrYXJ5b3RpYyBzYW1wbGUgSURzCm94eWNsaW5ldHlwZXNfZXVrIDwtIG1ldGFkYXRhICU+JSAKICBmaWx0ZXIoYFNhbXBsZSBOYW1lYCAlaW4lIHNhbXBsZV9uYW1lcyhwcykpICU+JQogIGZpbHRlcihPeENvbmQgPT0gIk94eWNsaW5lIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQpveHljbGluZXR5cGVzX2V1ayA8LSB1bmxpc3QoYyh1bmlxdWUob3h5Y2xpbmV0eXBlc19ldWspKSwgdXNlLm5hbWVzID0gRkFMU0UpCgojIFB1bGwgb3V0IGFsbCBldWthcnlvdGVzIGZyb20gb3h5Y2xpbmUKcHNfZXVrX294eWNsaW5lIDwtICBwcnVuZV9zYW1wbGVzKG94eWNsaW5ldHlwZXNfZXVrLCBwcykKcHNfZXVrX3JhX294eWNsaW5lIDwtICBwcnVuZV9zYW1wbGVzKG94eWNsaW5ldHlwZXNfZXVrLCBwc19yYSkKCmBgYAoKRmlsdGVyIG91dCBsb3cgYWJ1bmRhbmNlIHRheGEgZnJvbSB0aGUgb3h5Y2xpbmUgc2FtcGxlcy4gVXNlIDUlIGFzIGN1dG9mZiAKYGBge3J9CiMgQmFjdGVyaWEKeCA8LSB0YXhhX3N1bXMocHNfYmFjX3JhX294eWNsaW5lKQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2JhY19yYV9veHljbGluZV9wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2JhY19yYV9veHljbGluZSkKcHNfYmFjX294eWNsaW5lX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYmFjX294eWNsaW5lKQpwc19iYWNfcmFfb3h5Y2xpbmVfcHJ1bmVkCnBzX2JhY19veHljbGluZV9wcnVuZWQKCiMgQXJjaGFlYQp4IDwtIHRheGFfc3Vtcyhwc19hcmNoX3JhX294eWNsaW5lKQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2FyY2hfcmFfb3h5Y2xpbmVfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19hcmNoX3JhX294eWNsaW5lKQpwc19hcmNoX294eWNsaW5lX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYXJjaF9veHljbGluZSkKcHNfYXJjaF9yYV9veHljbGluZV9wcnVuZWQKcHNfYXJjaF9veHljbGluZV9wcnVuZWQKCiMgRXVrYXJ5b3Rlcwp4IDwtIHRheGFfc3Vtcyhwc19ldWtfcmFfb3h5Y2xpbmUpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfZXVrX3JhX294eWNsaW5lX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfZXVrX3JhX294eWNsaW5lKQpwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19ldWtfb3h5Y2xpbmUpCnBzX2V1a19yYV9veHljbGluZV9wcnVuZWQKcHNfZXVrX294eWNsaW5lX3BydW5lZAoKYGBgCjc5IGJhY3RlcmlhLCAzNiBhcmNoYWVhLCA3NiBldWthcnlvdGEgcmVtYWluCgoKQ2hhbmdlIHRoZSBzYW1wbGUgbmFtZXMgaW4gdGhlIG90dSB0YWJsZXMgdG8gIlR5cGUiCmBgYHtyfQojIEFyY2hhZWEKIyByZW1vdmUgbWlzc2luZyBhcmNoYWVhIHNhbXBsZXMgZnJvbSBzYW1wbGVrZXlfQQpzYW1wbGVrZXlfQSA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9hcmNoICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19hcmNoX3JhX294eWNsaW5lX3BydW5lZCkpKQojIHNvcnQgU2FtcGxlS2V5IGJ5IG9yZGVyIG9mIGNvbHVtbiBuYW1lcyBmcm9tIHBzX2FyY2hfcmFfb3h5Y2xpbmVfcHJ1bmVkCnNhbXBsZWtleV9BIDwtIHNhbXBsZWtleV9BICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRF9hcmNoLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYXJjaF9yYV9veHljbGluZV9wcnVuZWQpKSkpCiMgcmVwbGFjZSBjb2wgbmFtZXMgb2Ygb3R1IHRhYmxlIGZyb20gcHNfYXJjaF9yYV9veHljbGluZV9wcnVuZWQKc2FtcGxlX25hbWVzKHBzX2FyY2hfcmFfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQSRUeXBlCiMgYW5kIHBzX2FyY2hfcHJ1bmVkCnNhbXBsZV9uYW1lcyhwc19hcmNoX294eWNsaW5lX3BydW5lZCkgPC0gc2FtcGxla2V5X0EkVHlwZQoKCiMgQmFjdGVyaWEKc2FtcGxla2V5X0IgPC0gZmlsdGVyKHNhbXBsZWtleSwgU2FtcGxlSURfYmFjICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19iYWNfcmFfb3h5Y2xpbmVfcHJ1bmVkKSkpCnNhbXBsZWtleV9CIDwtIHNhbXBsZWtleV9CICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRF9iYWMsIGxldmVscyA9IGNvbG5hbWVzKG90dV90YWJsZShwc19iYWNfcmFfb3h5Y2xpbmVfcHJ1bmVkKSkpKQpzYW1wbGVfbmFtZXMocHNfYmFjX3JhX294eWNsaW5lX3BydW5lZCkgPC0gc2FtcGxla2V5X0IkVHlwZQpzYW1wbGVfbmFtZXMocHNfYmFjX294eWNsaW5lX3BydW5lZCkgPC0gc2FtcGxla2V5X0IkVHlwZQoKCiMgRXVrYXJ5b3RlcwpzYW1wbGVrZXlfRSA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9ldWsgICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2V1a19yYV9veHljbGluZV9wcnVuZWQpKSkKc2FtcGxla2V5X0UgPC0gc2FtcGxla2V5X0UgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2V1aywgbGV2ZWxzID0gY29sbmFtZXMob3R1X3RhYmxlKHBzX2V1a19yYV9veHljbGluZV9wcnVuZWQpKSkpCnNhbXBsZV9uYW1lcyhwc19ldWtfcmFfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfRSRUeXBlCnNhbXBsZV9uYW1lcyhwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfRSRUeXBlCgpgYGAKCgpNb3ZlIGFsbCBwcnVuZWQgb3R1IHRhYmxlcyBpbnRvIG9uZSB0YWJsZSBieSBtYXRjaGluZyB0aGUgc2FtcGxlIFR5cGUtIHdpbGwgdXNlIHRoaXMgZm9yIFNwYXJDQwpgYGB7cn0KYWxsZG9tYWluc19kZl9veHljbGluZSA8LSBiaW5kX3Jvd3MoZGF0YS5mcmFtZShvdHVfdGFibGUocHNfYmFjX294eWNsaW5lX3BydW5lZCkpLCBkYXRhLmZyYW1lKG90dV90YWJsZShwc19hcmNoX294eWNsaW5lX3BydW5lZCkpLCBkYXRhLmZyYW1lKG90dV90YWJsZShwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSkpCmFsbGRvbWFpbnNfZGZfb3h5Y2xpbmUKYGBgCgpDaGFuZ2Ugcm93IG5hbWVzIGZyb20gImRlbm92b1hYWCIgdG8gbWVhbmluZ2Z1bCBuYW1lcwpgYGB7cn0KYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lIDwtIGNiaW5kKElEID0gcm93bmFtZXMoYWxsZG9tYWluc19kZl9veHljbGluZSksIGFsbGRvbWFpbnNfZGZfb3h5Y2xpbmUpCgojIHN0YXJ0IHdpdGggb25seSBmaXJzdCByb3dzLCB3aGljaCBhcmUgYmFjdGVyaWEuIG1ha2Ugb25lIGNvbHVtbiBvZiBtZWFuaW5nZnVsIGxhYmVscwp0ZW1wMSA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lWzE6ZGltKG90dV90YWJsZShwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSlbMV0sXSwgYmFjX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjT1RVIElEIikpIAp0ZW1wMSROZXdfSUQgPC0gcGFzdGUodGVtcDEkSUQsIHRlbXAxJCJ0YXhvbm9teS0yIiwgdGVtcDEkInRheG9ub215LTMiLCB0ZW1wMSQidGF4b25vbXktNCIpCnRlbXAxIDwtIHNlbGVjdCh0ZW1wMSwtY29sbmFtZXMoYmFjX3RheG9ub215WywyOjExXSkpCgojIG5leHQgcm93cyBhcmUgdGhlIGFyY2hhZWEgCnRlbXAyIDwtIGxlZnRfam9pbihhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmVbc3VtKGRpbShvdHVfdGFibGUocHNfYmFjX294eWNsaW5lX3BydW5lZCkpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19veHljbGluZV9wcnVuZWQpKVsxXSxkaW0ob3R1X3RhYmxlKHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkKSlbMV0pLF0sIGFyY2hfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNPVFUgSUQiKSkgCnRlbXAyJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMiRJRCwgdGVtcDIkInRheG9ub215LTIiLCB0ZW1wMiQidGF4b25vbXktMyIpCnRlbXAyIDwtIHNlbGVjdCh0ZW1wMiwtY29sbmFtZXMoYXJjaF90YXhvbm9teVssMjo5XSkpCgoKIyBsYXN0IHJvd3MgYXJlIGV1a2FyeWEKZXVrX3RheG9ub215IDwtIGNiaW5kKCIjQVNWIElEIiA9IHJvd25hbWVzKHRheG9ub215KSwgdGF4b25vbXkpCnRlbXAzIDwtIGxlZnRfam9pbihhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmVbc3VtKGRpbShvdHVfdGFibGUocHNfYXJjaF9veHljbGluZV9wcnVuZWQpKVsxXSwgZGltKG90dV90YWJsZShwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSlbMV0sMSk6c3VtKGRpbShvdHVfdGFibGUocHNfYXJjaF9veHljbGluZV9wcnVuZWQpKVsxXSwgZGltKG90dV90YWJsZShwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSlbMV0sZGltKG90dV90YWJsZShwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSlbMV0pLF0sIGV1a190YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI0FTViBJRCIpKSAKdGVtcDMkTmV3X0lEIDwtIHBhc3RlKHRlbXAzJElELCB0ZW1wMyQiU3VwZXJncm91cCIsIHRlbXAzJCJEaXZpc2lvbiIsIHRlbXAzJCJDbGFzcyIsIHRlbXAzJCJPcmRlciIpCnRlbXAzIDwtIHNlbGVjdCh0ZW1wMywtY29sbmFtZXMoZXVrX3RheG9ub215WywyOjldKSkKCiMgY29tYmluZSBiYWNrIGFsbCAzIGRvbWFpbnMsIHdpdGggbmV3IG5hbWVzIGFzIHJvdyBuYW1lcyBpbiBhIGRhdGFmcmFtZQphbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUgPC0gcmJpbmQodGVtcDEsIHRlbXAyLCB0ZW1wMykKYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lIDwtIGRhdGEuZnJhbWUoYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lKQpyb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUpIDwtIGFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZSROZXdfSUQKYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lIDwtIHNlbGVjdChhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUsIC1jKCJJRCIsIk5ld19JRCIpKQphbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUKYGBgCgoKClJlbW92ZSBjb2x1bW5zIHdpdGggTkFzLiBUaGVzZSBhcmUgc2FtcGxlcyBmb3Igd2hpY2ggdGhlIGxpYnJhcnkgZm9yIGF0IGxlYXN0IG9uZSBkb21haW4gZGlkbid0IHdvcmsgKGNhbid0IGRvIGNvcnJlbGF0aW9ucyB3aXRoIG1pc3NpbmcgdmFsdWVzIGluIGNvbHVtbnMpCmBgYHtyfQphbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUgPC0gYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCmFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZQoKYWxsZG9tYWluc19kZl9veHljbGluZSA8LSBhbGxkb21haW5zX2RmX294eWNsaW5lICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCmFsbGRvbWFpbnNfZGZfb3h5Y2xpbmUKYGBgCjIxIHNhbXBsZXMgcmVtYWluIGZvciBjb3JyZWxhdGlvbgoKCgoKCgoKCiMjIyBGaWx0ZXIgbG93IGFidW5kYW5jZSBzcGVjaWVzLSBhbm94aWMgZGVwdGhzIG9ubHkKClB1bGwgb3V0IHNhbXBsZXMgZnJvbSBzaGFsbG93IGFub3hpYyByZWdpbWUKYGBge3J9CiMgUHVsbCBvdXQgYW5veGljIGxheWVyIGJhY3RlcmlhIHNhbXBsZSBJRHMKYW5veGljdHlwZXNfYmFjIDwtIG1ldGFkYXRhICU+JSAKICBmaWx0ZXIoYFNhbXBsZSBOYW1lYCAlaW4lIHNhbXBsZV9uYW1lcyhwc19iYWMpKSAlPiUKICBmaWx0ZXIoT3hDb25kID09ICJTaGFsbG93QW5veGljIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQphbm94aWN0eXBlc19iYWMgPC0gdW5saXN0KGModW5pcXVlKGFub3hpY3R5cGVzX2JhYykpLCB1c2UubmFtZXMgPSBGQUxTRSkKCiMgUHVsbCBvdXQgYWxsIGJhY3RlcmlhIGZyb20gYW5veGljIGxheWVyCnBzX2JhY19hbm94aWMgPC0gIHBydW5lX3NhbXBsZXMoYW5veGljdHlwZXNfYmFjLCBwc19iYWMpCnBzX2JhY19yYV9hbm94aWMgPC0gIHBydW5lX3NhbXBsZXMoYW5veGljdHlwZXNfYmFjLCBwc19iYWNfcmEpCgoKIyBQdWxsIG91dCBhbm94aWMgbGF5ZXIgYXJjaGFlYSBzYW1wbGUgSURzCmFub3hpY3R5cGVzX2FyY2ggPC0gbWV0YWRhdGEgJT4lIAogIGZpbHRlcihgU2FtcGxlIE5hbWVgICVpbiUgc2FtcGxlX25hbWVzKHBzX2FyY2gpKSAlPiUKICBmaWx0ZXIoT3hDb25kID09ICJTaGFsbG93QW5veGljIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQphbm94aWN0eXBlc19hcmNoIDwtIHVubGlzdChjKHVuaXF1ZShhbm94aWN0eXBlc19hcmNoKSksIHVzZS5uYW1lcyA9IEZBTFNFKQoKIyBQdWxsIG91dCBhbGwgYXJjaGFlYSBmcm9tIGFub3hpYyBsYXllcgpwc19hcmNoX2Fub3hpYzwtICBwcnVuZV9zYW1wbGVzKGFub3hpY3R5cGVzX2FyY2gsIHBzX2FyY2gpCnBzX2FyY2hfcmFfYW5veGljIDwtICBwcnVuZV9zYW1wbGVzKGFub3hpY3R5cGVzX2FyY2gsIHBzX2FyY2hfcmEpCgoKIyBQdWxsIG91dCBhbm94aWMgbGF5ZXIgZXVrYXJ5b3RpYyBzYW1wbGUgSURzCmFub3hpY3R5cGVzX2V1ayA8LSBtZXRhZGF0YSAlPiUgCiAgZmlsdGVyKGBTYW1wbGUgTmFtZWAgJWluJSBzYW1wbGVfbmFtZXMocHMpKSAlPiUKICBmaWx0ZXIoT3hDb25kID09ICJTaGFsbG93QW5veGljIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQphbm94aWN0eXBlc19ldWsgPC0gdW5saXN0KGModW5pcXVlKGFub3hpY3R5cGVzX2V1aykpLCB1c2UubmFtZXMgPSBGQUxTRSkKCiMgUHVsbCBvdXQgYWxsIGV1a2FyeW90ZXMgZnJvbSBhbm94aWMgbGF5ZXIKcHNfZXVrX2Fub3hpYyA8LSAgcHJ1bmVfc2FtcGxlcyhhbm94aWN0eXBlc19ldWssIHBzKQpwc19ldWtfcmFfYW5veGljIDwtICBwcnVuZV9zYW1wbGVzKGFub3hpY3R5cGVzX2V1aywgcHNfcmEpCgpgYGAKCkZpbHRlciBvdXQgbG93IGFidW5kYW5jZSB0YXhhIGZyb20gdGhlIG94eWNsaW5lIHNhbXBsZXMuIFVzZSA1JSBhcyBjdXRvZmYgCmBgYHtyfQojIEJhY3RlcmlhCnggPC0gdGF4YV9zdW1zKHBzX2JhY19yYV9hbm94aWMpCmtlZXBUYXhhIDwtICB4Pi4wNSAjIHBydW5lX3RheGEgcmVxdWlyZSBhIGxvZ2ljYWwgbm90IGEgbGlzdCBvZiBJRHMuIGNvbXBhcmUgdG8ga2VlcFRheGEgYWJvdmUgdG8gY2hlY2sKcHNfYmFjX3JhX2Fub3hpY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2JhY19yYV9hbm94aWMpCnBzX2JhY19hbm94aWNfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19iYWNfYW5veGljKQpwc19iYWNfcmFfYW5veGljX3BydW5lZApwc19iYWNfYW5veGljX3BydW5lZAoKIyBBcmNoYWVhCnggPC0gdGF4YV9zdW1zKHBzX2FyY2hfcmFfYW5veGljKQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2FyY2hfcmFfYW5veGljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYXJjaF9yYV9hbm94aWMpCnBzX2FyY2hfYW5veGljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYXJjaF9hbm94aWMpCnBzX2FyY2hfcmFfYW5veGljX3BydW5lZApwc19hcmNoX2Fub3hpY19wcnVuZWQKCiMgRXVrYXJ5b3Rlcwp4IDwtIHRheGFfc3Vtcyhwc19ldWtfcmFfYW5veGljKQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2V1a19yYV9hbm94aWNfcHJ1bmVkIDwtICBwcnVuZV90YXhhKGtlZXBUYXhhLCBwc19ldWtfcmFfYW5veGljKQpwc19ldWtfYW5veGljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfZXVrX2Fub3hpYykKcHNfZXVrX3JhX2Fub3hpY19wcnVuZWQKcHNfZXVrX2Fub3hpY19wcnVuZWQKCmBgYAozMiBiYWN0ZXJpYSwgMTkgYXJjaGFlYSwgMzcgZXVrYXJ5b3RhIHJlbWFpbgoKCkNoYW5nZSB0aGUgc2FtcGxlIG5hbWVzIGluIHRoZSBvdHUgdGFibGVzIHRvICJUeXBlIgpgYGB7cn0KIyBBcmNoYWVhCiMgcmVtb3ZlIG1pc3NpbmcgYXJjaGFlYSBzYW1wbGVzIGZyb20gc2FtcGxla2V5X0EKc2FtcGxla2V5X0EgPC0gZmlsdGVyKHNhbXBsZWtleSwgU2FtcGxlSURfYXJjaCAgJWluJSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYXJjaF9yYV9hbm94aWNfcHJ1bmVkKSkpCiMgc29ydCBTYW1wbGVLZXkgYnkgb3JkZXIgb2YgY29sdW1uIG5hbWVzIGZyb20gcHNfYXJjaF9yYV9hbm94aWNfcHJ1bmVkCnNhbXBsZWtleV9BIDwtIHNhbXBsZWtleV9BICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRF9hcmNoLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYXJjaF9yYV9hbm94aWNfcHJ1bmVkKSkpKQojIHJlcGxhY2UgY29sIG5hbWVzIG9mIG90dSB0YWJsZSBmcm9tIHBzX2FyY2hfcmFfYW5veGljX3BydW5lZApzYW1wbGVfbmFtZXMocHNfYXJjaF9yYV9hbm94aWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQSRUeXBlCiMgYW5kIHBzX2FyY2hfcHJ1bmVkCnNhbXBsZV9uYW1lcyhwc19hcmNoX2Fub3hpY19wcnVuZWQpIDwtIHNhbXBsZWtleV9BJFR5cGUKCgojIEJhY3RlcmlhCnNhbXBsZWtleV9CIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2JhYyAgJWluJSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYmFjX3JhX2Fub3hpY19wcnVuZWQpKSkKc2FtcGxla2V5X0IgPC0gc2FtcGxla2V5X0IgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2JhYywgbGV2ZWxzID0gY29sbmFtZXMob3R1X3RhYmxlKHBzX2JhY19yYV9hbm94aWNfcHJ1bmVkKSkpKQpzYW1wbGVfbmFtZXMocHNfYmFjX3JhX2Fub3hpY19wcnVuZWQpIDwtIHNhbXBsZWtleV9CJFR5cGUKc2FtcGxlX25hbWVzKHBzX2JhY19hbm94aWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQiRUeXBlCgoKIyBFdWthcnlvdGVzCnNhbXBsZWtleV9FIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2V1ayAgJWluJSBjb2xuYW1lcyhvdHVfdGFibGUocHNfZXVrX3JhX2Fub3hpY19wcnVuZWQpKSkKc2FtcGxla2V5X0UgPC0gc2FtcGxla2V5X0UgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2V1aywgbGV2ZWxzID0gY29sbmFtZXMob3R1X3RhYmxlKHBzX2V1a19yYV9hbm94aWNfcHJ1bmVkKSkpKQpzYW1wbGVfbmFtZXMocHNfZXVrX3JhX2Fub3hpY19wcnVuZWQpIDwtIHNhbXBsZWtleV9FJFR5cGUKc2FtcGxlX25hbWVzKHBzX2V1a19hbm94aWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfRSRUeXBlCgpgYGAKCgpNb3ZlIGFsbCBwcnVuZWQgb3R1IHRhYmxlcyBpbnRvIG9uZSB0YWJsZSBieSBtYXRjaGluZyB0aGUgc2FtcGxlIFR5cGUtIHdpbGwgdXNlIHRoaXMgZm9yIFNwYXJDQwpgYGB7cn0KYWxsZG9tYWluc19kZl9hbm94aWMgPC0gYmluZF9yb3dzKGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2JhY19hbm94aWNfcHJ1bmVkKSksIGRhdGEuZnJhbWUob3R1X3RhYmxlKHBzX2FyY2hfYW5veGljX3BydW5lZCkpLCBkYXRhLmZyYW1lKG90dV90YWJsZShwc19ldWtfYW5veGljX3BydW5lZCkpKQphbGxkb21haW5zX2RmX2Fub3hpYwpgYGAKCkNoYW5nZSByb3cgbmFtZXMgZnJvbSAiZGVub3ZvWFhYIiB0byBtZWFuaW5nZnVsIG5hbWVzCmBgYHtyfQphbGxkb21haW5zX2RmX2Z1bGxfYW5veGljIDwtIGNiaW5kKElEID0gcm93bmFtZXMoYWxsZG9tYWluc19kZl9hbm94aWMpLCBhbGxkb21haW5zX2RmX2Fub3hpYykKCiMgc3RhcnQgd2l0aCBvbmx5IGZpcnN0IHJvd3MsIHdoaWNoIGFyZSBiYWN0ZXJpYS4gbWFrZSBvbmUgY29sdW1uIG9mIG1lYW5pbmdmdWwgbGFiZWxzCnRlbXAxIDwtIGxlZnRfam9pbihhbGxkb21haW5zX2RmX2Z1bGxfYW5veGljWzE6ZGltKG90dV90YWJsZShwc19iYWNfYW5veGljX3BydW5lZCkpWzFdLF0sIGJhY190YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI09UVSBJRCIpKSAKdGVtcDEkTmV3X0lEIDwtIHBhc3RlKHRlbXAxJElELCB0ZW1wMSQidGF4b25vbXktMiIsIHRlbXAxJCJ0YXhvbm9teS0zIiwgdGVtcDEkInRheG9ub215LTQiKQp0ZW1wMSA8LSBzZWxlY3QodGVtcDEsLWNvbG5hbWVzKGJhY190YXhvbm9teVssMjoxMV0pKQoKIyBuZXh0IHJvd3MgYXJlIHRoZSBhcmNoYWVhIAp0ZW1wMiA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpY1tzdW0oZGltKG90dV90YWJsZShwc19iYWNfYW5veGljX3BydW5lZCkpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19hbm94aWNfcHJ1bmVkKSlbMV0sZGltKG90dV90YWJsZShwc19hcmNoX2Fub3hpY19wcnVuZWQpKVsxXSksXSwgYXJjaF90YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI09UVSBJRCIpKSAKdGVtcDIkTmV3X0lEIDwtIHBhc3RlKHRlbXAyJElELCB0ZW1wMiQidGF4b25vbXktMiIsIHRlbXAyJCJ0YXhvbm9teS0zIikKdGVtcDIgPC0gc2VsZWN0KHRlbXAyLC1jb2xuYW1lcyhhcmNoX3RheG9ub215WywyOjldKSkKCgojIGxhc3Qgcm93cyBhcmUgZXVrYXJ5YQpldWtfdGF4b25vbXkgPC0gY2JpbmQoIiNBU1YgSUQiID0gcm93bmFtZXModGF4b25vbXkpLCB0YXhvbm9teSkKdGVtcDMgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWNbc3VtKGRpbShvdHVfdGFibGUocHNfYXJjaF9hbm94aWNfcHJ1bmVkKSlbMV0sIGRpbShvdHVfdGFibGUocHNfYmFjX2Fub3hpY19wcnVuZWQpKVsxXSwxKTpzdW0oZGltKG90dV90YWJsZShwc19hcmNoX2Fub3hpY19wcnVuZWQpKVsxXSwgZGltKG90dV90YWJsZShwc19iYWNfYW5veGljX3BydW5lZCkpWzFdLGRpbShvdHVfdGFibGUocHNfZXVrX2Fub3hpY19wcnVuZWQpKVsxXSksXSwgZXVrX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjQVNWIElEIikpIAp0ZW1wMyROZXdfSUQgPC0gcGFzdGUodGVtcDMkSUQsIHRlbXAzJCJTdXBlcmdyb3VwIiwgdGVtcDMkIkRpdmlzaW9uIiwgdGVtcDMkIkNsYXNzIiwgdGVtcDMkIk9yZGVyIikKdGVtcDMgPC0gc2VsZWN0KHRlbXAzLC1jb2xuYW1lcyhldWtfdGF4b25vbXlbLDI6OV0pKQoKIyBjb21iaW5lIGJhY2sgYWxsIDMgZG9tYWlucywgd2l0aCBuZXcgbmFtZXMgYXMgcm93IG5hbWVzIGluIGEgZGF0YWZyYW1lCmFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMgPC0gcmJpbmQodGVtcDEsIHRlbXAyLCB0ZW1wMykKYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYyA8LSBkYXRhLmZyYW1lKGFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMpCnJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMpIDwtIGFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMkTmV3X0lECmFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMgPC0gc2VsZWN0KGFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMsIC1jKCJJRCIsIk5ld19JRCIpKQphbGxkb21haW5zX2RmX2Z1bGxfYW5veGljCmBgYAoKCgpSZW1vdmUgY29sdW1ucyB3aXRoIE5Bcy4gVGhlc2UgYXJlIHNhbXBsZXMgZm9yIHdoaWNoIHRoZSBsaWJyYXJ5IGZvciBhdCBsZWFzdCBvbmUgZG9tYWluIGRpZG4ndCB3b3JrIChjYW4ndCBkbyBjb3JyZWxhdGlvbnMgd2l0aCBtaXNzaW5nIHZhbHVlcyBpbiBjb2x1bW5zKQpgYGB7cn0KYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYyA8LSBhbGxkb21haW5zX2RmX2Z1bGxfYW5veGljICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCmFsbGRvbWFpbnNfZGZfZnVsbF9hbm94aWMKCmFsbGRvbWFpbnNfZGZfYW5veGljIDwtIGFsbGRvbWFpbnNfZGZfYW5veGljICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCmFsbGRvbWFpbnNfZGZfYW5veGljCmBgYAoxMSBzYW1wbGVzIHJlbWFpbiBmb3IgY29ycmVsYXRpb24KCgoKCgojIyMgRmlsdGVyIGxvdyBhYnVuZGFuY2Ugc3BlY2llcy0gZXV4aW5pYyBkZXB0aHMgb25seQoKUHVsbCBvdXQgc2FtcGxlcyBmcm9tIHNoYWxsb3cgYW5veGljIHJlZ2ltZQpgYGB7cn0KIyBQdWxsIG91dCBhbm94aWMgbGF5ZXIgYmFjdGVyaWEgc2FtcGxlIElEcwpldXhpbmljdHlwZXNfYmFjIDwtIG1ldGFkYXRhICU+JSAKICBmaWx0ZXIoYFNhbXBsZSBOYW1lYCAlaW4lIHNhbXBsZV9uYW1lcyhwc19iYWMpKSAlPiUKICBmaWx0ZXIoT3hDb25kID09ICJFdXhpbmljIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQpldXhpbmljdHlwZXNfYmFjIDwtIHVubGlzdChjKHVuaXF1ZShldXhpbmljdHlwZXNfYmFjKSksIHVzZS5uYW1lcyA9IEZBTFNFKQoKIyBQdWxsIG91dCBhbGwgYmFjdGVyaWEgZnJvbSBldXhpbmljIGxheWVyCnBzX2JhY19ldXhpbmljIDwtICBwcnVuZV9zYW1wbGVzKGV1eGluaWN0eXBlc19iYWMsIHBzX2JhYykKcHNfYmFjX3JhX2V1eGluaWMgPC0gIHBydW5lX3NhbXBsZXMoZXV4aW5pY3R5cGVzX2JhYywgcHNfYmFjX3JhKQoKCiMgUHVsbCBvdXQgZXV4aW5pYyBsYXllciBhcmNoYWVhIHNhbXBsZSBJRHMKZXV4aW5pY3R5cGVzX2FyY2ggPC0gbWV0YWRhdGEgJT4lIAogIGZpbHRlcihgU2FtcGxlIE5hbWVgICVpbiUgc2FtcGxlX25hbWVzKHBzX2FyY2gpKSAlPiUKICBmaWx0ZXIoT3hDb25kID09ICJFdXhpbmljIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQpldXhpbmljdHlwZXNfYXJjaCA8LSB1bmxpc3QoYyh1bmlxdWUoZXV4aW5pY3R5cGVzX2FyY2gpKSwgdXNlLm5hbWVzID0gRkFMU0UpCgojIFB1bGwgb3V0IGFsbCBhcmNoYWVhIGZyb20gZXV4aW5pYyBsYXllcgpwc19hcmNoX2V1eGluaWM8LSAgcHJ1bmVfc2FtcGxlcyhldXhpbmljdHlwZXNfYXJjaCwgcHNfYXJjaCkKcHNfYXJjaF9yYV9ldXhpbmljIDwtICBwcnVuZV9zYW1wbGVzKGV1eGluaWN0eXBlc19hcmNoLCBwc19hcmNoX3JhKQoKCiMgUHVsbCBvdXQgZXV4aW5pYyBsYXllciBldWthcnlvdGljIHNhbXBsZSBJRHMKZXV4aW5pY3R5cGVzX2V1ayA8LSBtZXRhZGF0YSAlPiUgCiAgZmlsdGVyKGBTYW1wbGUgTmFtZWAgJWluJSBzYW1wbGVfbmFtZXMocHMpKSAlPiUKICBmaWx0ZXIoT3hDb25kID09ICJFdXhpbmljIikgJT4lIAogIHNlbGVjdCgiU2FtcGxlIE5hbWUiKQpldXhpbmljdHlwZXNfZXVrIDwtIHVubGlzdChjKHVuaXF1ZShldXhpbmljdHlwZXNfZXVrKSksIHVzZS5uYW1lcyA9IEZBTFNFKQoKIyBQdWxsIG91dCBhbGwgZXVrYXJ5b3RlcyBmcm9tIGV1eGluaWMgbGF5ZXIKcHNfZXVrX2V1eGluaWMgPC0gIHBydW5lX3NhbXBsZXMoZXV4aW5pY3R5cGVzX2V1aywgcHMpCnBzX2V1a19yYV9ldXhpbmljIDwtICBwcnVuZV9zYW1wbGVzKGV1eGluaWN0eXBlc19ldWssIHBzX3JhKQoKYGBgCgpGaWx0ZXIgb3V0IGxvdyBhYnVuZGFuY2UgdGF4YSBmcm9tIHRoZSBveHljbGluZSBzYW1wbGVzLiBVc2UgNSUgYXMgY3V0b2ZmIApgYGB7cn0KIyBCYWN0ZXJpYQp4IDwtIHRheGFfc3Vtcyhwc19iYWNfcmFfZXV4aW5pYykKa2VlcFRheGEgPC0gIHg+LjA1ICMgcHJ1bmVfdGF4YSByZXF1aXJlIGEgbG9naWNhbCBub3QgYSBsaXN0IG9mIElEcy4gY29tcGFyZSB0byBrZWVwVGF4YSBhYm92ZSB0byBjaGVjawpwc19iYWNfcmFfZXV4aW5pY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2JhY19yYV9ldXhpbmljKQpwc19iYWNfZXV4aW5pY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2JhY19ldXhpbmljKQpwc19iYWNfcmFfZXV4aW5pY19wcnVuZWQKcHNfYmFjX2V1eGluaWNfcHJ1bmVkCgojIEFyY2hhZWEKeCA8LSB0YXhhX3N1bXMocHNfYXJjaF9yYV9ldXhpbmljKQprZWVwVGF4YSA8LSAgeD4uMDUgIyBwcnVuZV90YXhhIHJlcXVpcmUgYSBsb2dpY2FsIG5vdCBhIGxpc3Qgb2YgSURzLiBjb21wYXJlIHRvIGtlZXBUYXhhIGFib3ZlIHRvIGNoZWNrCnBzX2FyY2hfcmFfZXV4aW5pY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2FyY2hfcmFfZXV4aW5pYykKcHNfYXJjaF9ldXhpbmljX3BydW5lZCA8LSAgcHJ1bmVfdGF4YShrZWVwVGF4YSwgcHNfYXJjaF9ldXhpbmljKQpwc19hcmNoX3JhX2V1eGluaWNfcHJ1bmVkCnBzX2FyY2hfZXV4aW5pY19wcnVuZWQKCiMgRXVrYXJ5b3Rlcwp4IDwtIHRheGFfc3Vtcyhwc19ldWtfcmFfZXV4aW5pYykKa2VlcFRheGEgPC0gIHg+LjA1ICMgcHJ1bmVfdGF4YSByZXF1aXJlIGEgbG9naWNhbCBub3QgYSBsaXN0IG9mIElEcy4gY29tcGFyZSB0byBrZWVwVGF4YSBhYm92ZSB0byBjaGVjawpwc19ldWtfcmFfZXV4aW5pY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2V1a19yYV9ldXhpbmljKQpwc19ldWtfZXV4aW5pY19wcnVuZWQgPC0gIHBydW5lX3RheGEoa2VlcFRheGEsIHBzX2V1a19ldXhpbmljKQpwc19ldWtfcmFfZXV4aW5pY19wcnVuZWQKcHNfZXVrX2V1eGluaWNfcHJ1bmVkCgpgYGAKMTYgYmFjdGVyaWEsIDE2IGFyY2hhZWEsIDIwIGV1a2FyeW90YSByZW1haW4KCgpDaGFuZ2UgdGhlIHNhbXBsZSBuYW1lcyBpbiB0aGUgb3R1IHRhYmxlcyB0byAiVHlwZSIKYGBge3J9CiMgQXJjaGFlYQojIHJlbW92ZSBtaXNzaW5nIGFyY2hhZWEgc2FtcGxlcyBmcm9tIHNhbXBsZWtleV9BCnNhbXBsZWtleV9BIDwtIGZpbHRlcihzYW1wbGVrZXksIFNhbXBsZUlEX2FyY2ggICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2FyY2hfcmFfZXV4aW5pY19wcnVuZWQpKSkKIyBzb3J0IFNhbXBsZUtleSBieSBvcmRlciBvZiBjb2x1bW4gbmFtZXMgZnJvbSBwc19hcmNoX3JhX2V1eGluaWNfcHJ1bmVkCnNhbXBsZWtleV9BIDwtIHNhbXBsZWtleV9BICU+JSBhcnJhbmdlKGZhY3RvcihTYW1wbGVJRF9hcmNoLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYXJjaF9yYV9ldXhpbmljX3BydW5lZCkpKSkKIyByZXBsYWNlIGNvbCBuYW1lcyBvZiBvdHUgdGFibGUgZnJvbSBwc19hcmNoX3JhX2V1eGluaWNfcHJ1bmVkCnNhbXBsZV9uYW1lcyhwc19hcmNoX3JhX2V1eGluaWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQSRUeXBlCiMgYW5kIHBzX2FyY2hfcHJ1bmVkCnNhbXBsZV9uYW1lcyhwc19hcmNoX2V1eGluaWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQSRUeXBlCgoKIyBCYWN0ZXJpYQpzYW1wbGVrZXlfQiA8LSBmaWx0ZXIoc2FtcGxla2V5LCBTYW1wbGVJRF9iYWMgICVpbiUgY29sbmFtZXMob3R1X3RhYmxlKHBzX2JhY19yYV9ldXhpbmljX3BydW5lZCkpKQpzYW1wbGVrZXlfQiA8LSBzYW1wbGVrZXlfQiAlPiUgYXJyYW5nZShmYWN0b3IoU2FtcGxlSURfYmFjLCBsZXZlbHMgPSBjb2xuYW1lcyhvdHVfdGFibGUocHNfYmFjX3JhX2V1eGluaWNfcHJ1bmVkKSkpKQpzYW1wbGVfbmFtZXMocHNfYmFjX3JhX2V1eGluaWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfQiRUeXBlCnNhbXBsZV9uYW1lcyhwc19iYWNfZXV4aW5pY19wcnVuZWQpIDwtIHNhbXBsZWtleV9CJFR5cGUKCgojIEV1a2FyeW90ZXMKc2FtcGxla2V5X0UgPC0gZmlsdGVyKHNhbXBsZWtleSwgU2FtcGxlSURfZXVrICAlaW4lIGNvbG5hbWVzKG90dV90YWJsZShwc19ldWtfcmFfZXV4aW5pY19wcnVuZWQpKSkKc2FtcGxla2V5X0UgPC0gc2FtcGxla2V5X0UgJT4lIGFycmFuZ2UoZmFjdG9yKFNhbXBsZUlEX2V1aywgbGV2ZWxzID0gY29sbmFtZXMob3R1X3RhYmxlKHBzX2V1a19yYV9ldXhpbmljX3BydW5lZCkpKSkKc2FtcGxlX25hbWVzKHBzX2V1a19yYV9ldXhpbmljX3BydW5lZCkgPC0gc2FtcGxla2V5X0UkVHlwZQpzYW1wbGVfbmFtZXMocHNfZXVrX2V1eGluaWNfcHJ1bmVkKSA8LSBzYW1wbGVrZXlfRSRUeXBlCgpgYGAKCgpNb3ZlIGFsbCBwcnVuZWQgb3R1IHRhYmxlcyBpbnRvIG9uZSB0YWJsZSBieSBtYXRjaGluZyB0aGUgc2FtcGxlIFR5cGUtIHdpbGwgdXNlIHRoaXMgZm9yIFNwYXJDQwpgYGB7cn0KYWxsZG9tYWluc19kZl9ldXhpbmljIDwtIGJpbmRfcm93cyhkYXRhLmZyYW1lKG90dV90YWJsZShwc19iYWNfZXV4aW5pY19wcnVuZWQpKSwgZGF0YS5mcmFtZShvdHVfdGFibGUocHNfYXJjaF9ldXhpbmljX3BydW5lZCkpLCBkYXRhLmZyYW1lKG90dV90YWJsZShwc19ldWtfZXV4aW5pY19wcnVuZWQpKSkKYWxsZG9tYWluc19kZl9ldXhpbmljCmBgYAoKQ2hhbmdlIHJvdyBuYW1lcyBmcm9tICJkZW5vdm9YWFgiIHRvIG1lYW5pbmdmdWwgbmFtZXMKYGBge3J9CmFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljIDwtIGNiaW5kKElEID0gcm93bmFtZXMoYWxsZG9tYWluc19kZl9ldXhpbmljKSwgYWxsZG9tYWluc19kZl9ldXhpbmljKQoKIyBzdGFydCB3aXRoIG9ubHkgZmlyc3Qgcm93cywgd2hpY2ggYXJlIGJhY3RlcmlhLiBtYWtlIG9uZSBjb2x1bW4gb2YgbWVhbmluZ2Z1bCBsYWJlbHMKdGVtcDEgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljWzE6ZGltKG90dV90YWJsZShwc19iYWNfZXV4aW5pY19wcnVuZWQpKVsxXSxdLCBiYWNfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNPVFUgSUQiKSkgCnRlbXAxJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMSRJRCwgdGVtcDEkInRheG9ub215LTIiLCB0ZW1wMSQidGF4b25vbXktMyIsIHRlbXAxJCJ0YXhvbm9teS00IikKdGVtcDEgPC0gc2VsZWN0KHRlbXAxLC1jb2xuYW1lcyhiYWNfdGF4b25vbXlbLDI6MTFdKSkKCiMgbmV4dCByb3dzIGFyZSB0aGUgYXJjaGFlYSAKdGVtcDIgPC0gbGVmdF9qb2luKGFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljW3N1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19ldXhpbmljX3BydW5lZCkpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19ldXhpbmljX3BydW5lZCkpWzFdLGRpbShvdHVfdGFibGUocHNfYXJjaF9ldXhpbmljX3BydW5lZCkpWzFdKSxdLCBhcmNoX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjT1RVIElEIikpIAp0ZW1wMiROZXdfSUQgPC0gcGFzdGUodGVtcDIkSUQsIHRlbXAyJCJ0YXhvbm9teS0yIiwgdGVtcDIkInRheG9ub215LTMiKQp0ZW1wMiA8LSBzZWxlY3QodGVtcDIsLWNvbG5hbWVzKGFyY2hfdGF4b25vbXlbLDI6OV0pKQoKCiMgbGFzdCByb3dzIGFyZSBldWthcnlhCmV1a190YXhvbm9teSA8LSBjYmluZCgiI0FTViBJRCIgPSByb3duYW1lcyh0YXhvbm9teSksIHRheG9ub215KQp0ZW1wMyA8LSBsZWZ0X2pvaW4oYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWNbc3VtKGRpbShvdHVfdGFibGUocHNfYXJjaF9ldXhpbmljX3BydW5lZCkpWzFdLCBkaW0ob3R1X3RhYmxlKHBzX2JhY19ldXhpbmljX3BydW5lZCkpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2FyY2hfZXV4aW5pY19wcnVuZWQpKVsxXSwgZGltKG90dV90YWJsZShwc19iYWNfZXV4aW5pY19wcnVuZWQpKVsxXSxkaW0ob3R1X3RhYmxlKHBzX2V1a19ldXhpbmljX3BydW5lZCkpWzFdKSxdLCBldWtfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNBU1YgSUQiKSkgCnRlbXAzJE5ld19JRCA8LSBwYXN0ZSh0ZW1wMyRJRCwgdGVtcDMkIlN1cGVyZ3JvdXAiLCB0ZW1wMyQiRGl2aXNpb24iLCB0ZW1wMyQiQ2xhc3MiLCB0ZW1wMyQiT3JkZXIiKQp0ZW1wMyA8LSBzZWxlY3QodGVtcDMsLWNvbG5hbWVzKGV1a190YXhvbm9teVssMjo5XSkpCgojIGNvbWJpbmUgYmFjayBhbGwgMyBkb21haW5zLCB3aXRoIG5ldyBuYW1lcyBhcyByb3cgbmFtZXMgaW4gYSBkYXRhZnJhbWUKYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMgPC0gcmJpbmQodGVtcDEsIHRlbXAyLCB0ZW1wMykKYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMgPC0gZGF0YS5mcmFtZShhbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pYykKcm93bmFtZXMoYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMpIDwtIGFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljJE5ld19JRAphbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pYyA8LSBzZWxlY3QoYWxsZG9tYWluc19kZl9mdWxsX2V1eGluaWMsIC1jKCJJRCIsIk5ld19JRCIpKQphbGxkb21haW5zX2RmX2Z1bGxfZXV4aW5pYwpgYGAKCgoKUmVtb3ZlIGNvbHVtbnMgd2l0aCBOQXMuIFRoZXNlIGFyZSBzYW1wbGVzIGZvciB3aGljaCB0aGUgbGlicmFyeSBmb3IgYXQgbGVhc3Qgb25lIGRvbWFpbiBkaWRuJ3Qgd29yayAoY2FuJ3QgZG8gY29ycmVsYXRpb25zIHdpdGggbWlzc2luZyB2YWx1ZXMgaW4gY29sdW1ucykKYGBge3J9CmFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljIDwtIGFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCmFsbGRvbWFpbnNfZGZfZnVsbF9ldXhpbmljCgphbGxkb21haW5zX2RmX2V1eGluaWMgPC0gYWxsZG9tYWluc19kZl9ldXhpbmljICU+JQogICAgc2VsZWN0X2lmKH4gIWFueShpcy5uYSguKSkpCmFsbGRvbWFpbnNfZGZfZXV4aW5pYwpgYGAKNCBzYW1wbGVzIHJlbWFpbiBmb3IgY29ycmVsYXRpb24KCgoKCgoKIyMgU3BhckNDIAojIyMgU3BhckNDIG9uIGZ1bGwgZGF0YXNldApUaGlzIGlzIGxhcmdlbHkgYmFzZWQgb24gW0JWQ04gdHV0b3JpYWxzXShodHRwczovL2dpdGh1Yi5jb20vYmlvdmNuZXQvYmlvdmNuZXQuZ2l0aHViLmlvL3dpa2kvVE9QSUMlM0EtTmV0d29ya3MpCk5PVEUtIGlucHV0IGZvciBTcGFyQ0Mgc2hvdWxkIGJlIHJhdyBjb3VudCBkYXRhIChhZnRlciBmaWx0ZXJpbmcgb3V0IGxvdy1hYnVuZGFuY2UgQVNWcykuIFRoZSBmdW5jdGlvbiBkb2VzIGEgbG9nLXJhdGlvIHRyYW5zZm9ybWF0aW9uIHRvIGFjY291bnQgZm9yIGNvbXBvc2l0aW9uYWxpdHkKCmBgYHtyfQpzcGFyY2N0YWJsZV9hbGxkb21haW5zIDwtIHNwYXJjYyh0KGFsbGRvbWFpbnNfZGYpKQpgYGAKClB1dCBzYW1wbGUgbmFtZXMgYmFjayBpbnRvIHJlc3VsdCB0YWJsZXMKYGBge3J9CnJvd25hbWVzKHNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ29yKSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGwpCmNvbG5hbWVzKHNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ29yKSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGwpCnJvd25hbWVzKHNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ292KSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGwpCmNvbG5hbWVzKHNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ292KSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGwpCgpzcGFyY2N0YWJsZV9hbGxkb21haW5zJENvclsxOjIsMToyXQoKYGBgCgoKClBsb3QgY29ycmVsYXRpb24KYGBge3J9CnBsb3RhYmxlU3BhcmNjIDwtIHNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ29yICU+JSByZW9yZGVyX2Nvcm1hdCAlPiUgZ2V0X3VwcGVyX3RyaSgpICU+JSByZXNoYXBlMjo6bWVsdCgpICU+JSBuYS5vbWl0KCkKClNwYXJjY19wbG90IDwtIHBsb3RhYmxlU3BhcmNjICU+JSBnZ3Bsb3QoYWVzKHggPSBWYXIyLCB5ID0gVmFyMSwgZmlsbCA9IHZhbHVlKSkgKyBnZW9tX3RpbGUoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQoKU3BhcmNjX3Bsb3QKCiMgZ2dzYXZlKCJmaWd1cmVzL3NwYXJjY19jb3JyX2FsbGRvbWFpbnMuZXBzIixTcGFyY2NfcGxvdCwgd2lkdGggPSAzNSwgaGVpZ2h0ID0gMzUsIHVuaXRzID0gYygiaW4iKSkKYGBgCgoKCkNhbGN1bGF0ZSBTcGFyY2MgcC12YWx1ZXMgYnkgYm9vdHN0cmFwcGluZy0gVEFLRVMgQSBMT05HIFRJTUUKYGBge3J9CiMgdHAwIDwtIHByb2MudGltZSgpCiMgb3V0MiA8LSBzcGFyY2Nib290KHQoYWxsZG9tYWluc19kZiksIFIgPSAxMDAwLCBuY3B1cyA9IDIpCiMgdHAxIDwtIHByb2MudGltZSgpCiMgdHAxIC0gdHAwCmBgYApUaGUgYWJvdmUgdG9vayB+MTQgaG91cnMgdG8gcnVuIDEwMDAgaXRlcmF0aW9ucwoKCkV4dHJhY3QgcC12YWx1ZXMgCmBgYHtyfQpvdXRQIDwtIHB2YWwuc3BhcmNjYm9vdChvdXQyKQpkYXRhLmZyYW1lKG91dFAkY29ycywgb3V0UCRwdmFscykgJT4lIGhlYWQKY29ycyA8LSBvdXRQJGNvcnMKcHZhbHMgPC0gb3V0UCRwdmFscwpzcGFyQ0NwY29ycyA8LSBkaWFnKDAuNSwgbnJvdyA9IGRpbShzcGFyY2N0YWJsZV9hbGxkb21haW5zJENvcilbMV0sIG5jb2wgPSBkaW0oc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3IpWzFdKQpzcGFyQ0NwY29yc1t1cHBlci50cmkoc3BhckNDcGNvcnMsIGRpYWc9RkFMU0UpXSA8LSBjb3JzCnNwYXJDQ3Bjb3JzIDwtIHNwYXJDQ3Bjb3JzICsgdChzcGFyQ0NwY29ycykKCnNwYXJDQ3B2YWwgPC0gZGlhZygwLjUsIG5yb3cgPSBkaW0oc3BhcmNjdGFibGVfYWxsZG9tYWlucyRDb3IpWzFdLCBuY29sID0gZGltKHNwYXJjY3RhYmxlX2FsbGRvbWFpbnMkQ29yKVsxXSkKc3BhckNDcHZhbFt1cHBlci50cmkoc3BhckNDcHZhbCwgZGlhZz1GQUxTRSldIDwtIHB2YWxzCnNwYXJDQ3B2YWwgPC0gc3BhckNDcHZhbCArIHQoc3BhckNDcHZhbCkKCnJvd25hbWVzKHNwYXJDQ3Bjb3JzKSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGwpCmNvbG5hbWVzKHNwYXJDQ3Bjb3JzKSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGwpCnJvd25hbWVzKHNwYXJDQ3B2YWwpIDwtIHJvd25hbWVzKGFsbGRvbWFpbnNfZGZfZnVsbCkKY29sbmFtZXMoc3BhckNDcHZhbCkgPC0gcm93bmFtZXMoYWxsZG9tYWluc19kZl9mdWxsKQoKc3BhckNDcGNvcnNbMToyLCAxOjJdCnNwYXJDQ3B2YWxbMToyLCAxOjJdCmBgYAoKUmVvcmRlciBmb3IgcGxvdHRpbmcKYGBge3J9CnJlb3JkZXJlZF9hbGxfc3BhcmNjIDwtIHJlb3JkZXJfY29yX2FuZF9wKHNwYXJDQ3Bjb3JzLCBzcGFyQ0NwdmFsKQpyZW9yZGVyZWRfc3BhcmNjQ29yIDwtIHJlb3JkZXJlZF9hbGxfc3BhcmNjJHIKcmVvcmRlcmVkX3NwYXJjY1A8LSByZW9yZGVyZWRfYWxsX3NwYXJjYyRwCgoKc3BhcmNjQ29yX3Byb2Nlc3NlZCA8LSByZW9yZGVyZWRfc3BhcmNjQ29yICAlPiUgZ2V0X3VwcGVyX3RyaSgpICU+JSByZXNoYXBlMjo6bWVsdCgpICU+JSBuYS5vbWl0KCkgJT4lIHJlbmFtZShjb3IgPSB2YWx1ZSkKc3BhcmNjUF9wcm9jZXNzZWQgPC0gcmVvcmRlcmVkX3NwYXJjY1AgICU+JSBnZXRfdXBwZXJfdHJpKCkgJT4lIHJlc2hhcGUyOjptZWx0KCkgJT4lIG5hLm9taXQoKSAlPiUgcmVuYW1lKHAgPSB2YWx1ZSkKCiMgam9pbiB0aGUgdHdvIGRhdGEgZnJhbWVzCgpTcGFyY2NQIDwtIGxlZnRfam9pbihzcGFyY2NDb3JfcHJvY2Vzc2VkLCBzcGFyY2NQX3Byb2Nlc3NlZCwgYnkgPSBjKCJWYXIxIiwgIlZhcjIiKSkgJT4lCiAgIyAjIHJlbW92ZSBzZWxmIGNvcnJlbGF0aW9ucwogICMgZmlsdGVyKFZhcjEgIT0gVmFyMikgJT4lIAogICMgY2FsY3VsYXRlIHRoZSBmYWxzZSBkaXNjb3ZlcnkgcmF0ZSB0byBhZGp1c3QgZm9yIG11bHRpcGxlIHAgdmFsdWVzCiAgbXV0YXRlKGZkciA9IHAuYWRqdXN0KHAsIG1ldGhvZCA9ICJCSCIpKQpgYGAKCgpBbmQgcGxvdCBjb3JyZWxhdGlvbiB3aXRoIHAtdmFsdWVzLiBDaXJjbGVzIG1lYW4gdGhhdCB0aGUgcmVsYXRpb25zaGlwIGlzIHNpZy4gYXQgcCA9IDAuMDUgbGV2ZWwsIGJhc2VkIG9uIGJvb3RzdHJhcHBpbmcKYGBge3J9CmZkclRocmVzaCA8LSAwLjAxICMgZmRyIHRocmVzaG9sZApzcGFyY2NPa1AgPC0gU3BhcmNjUCU+JSBmaWx0ZXIoZmRyIDwgZmRyVGhyZXNoKSAKClNwYXJjY1BfcGxvdCA8LSBTcGFyY2NQICU+JSBnZ3Bsb3QoYWVzKHggPSBWYXIyLCB5ID0gVmFyMSwgZmlsbCA9IGNvcikpICsgZ2VvbV90aWxlKCkgKyBzY2FsZV9maWxsX2dyYWRpZW50MigpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkgKyBnZW9tX3BvaW50KGRhdGEgPSBzcGFyY2NPa1AsIHNoYXBlID0gMSkKClNwYXJjY1BfcGxvdAoKZ2dzYXZlKCJmaWd1cmVzL3NwYXJjY19jb3JyX2FsbGRvbWFpbnNfd19wdmFscy5lcHMiLFNwYXJjY1BfcGxvdCwgd2lkdGggPSAzNSwgaGVpZ2h0ID0gMzUsIHVuaXRzID0gYygiaW4iKSkKCmBgYAoKCgpTYXZlIGVudmlyb25tZW50IGFnYWluCmBgYHtyfQojIHNhdmUuaW1hZ2UoIkVudmlyb25tZW50QmFja3Vwcy9DYXJpYWNvRXVrc19wb3N0YW5hbHlzaXNfdmFyc191cHRvX3NwYXJjY19ib290c3RyYXAuUkRhdGEiKQpgYGAKCk9yIGxvYWQgaWYgY29taW5nIGJhY2sKYGBge3J9CmxvYWQoIkVudmlyb25tZW50QmFja3Vwcy9DYXJpYWNvRXVrc19wb3N0YW5hbHlzaXNfdmFyc191cHRvX3NwYXJjY19ib290c3RyYXAuUkRhdGEiKQpgYGAKCgoKCgojIyBTcGllY0Vhc2kKClRyeSB0aGUgU3BpZWNFYXNpIG1ldGhvZCwgd2hpY2ggYWNjb3VudHMgZm9yIHNwYXJzZSBkYXRhLCBhcyBkZXNjcmliZWQgaW4gdGhlIFtTcGllY0Vhc2kgcHVibGljYXRpb25dKGh0dHBzOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc2NvbXBiaW9sL2FydGljbGU/aWQ9MTAuMTM3MS9qb3VybmFsLnBjYmkuMTAwNDIyNiksIFtzcGllY2Vhc2kgZ2l0aHViXShodHRwczovL2dpdGh1Yi5jb20vemRrMTIzL1NwaWVjRWFzaSksIGFuZCBbQlZDTiBsZXNzb25zIDEuMl0oaHR0cHM6Ly9iaW92Y25ldC5naXRodWIuaW8vX3BhZ2VzL05ldHdvcmtTY2llbmNlX2dsYXNzbykuIFRoaXMgcmVkdWNlcyB0aGUgY2x1bXBzIChlZy4gc3BhcnNlIHJlbGF0aW9uc2hpcHMgdGhhdCBhcmUgc2Vjb25kYXJ5IG9yIHRlcmlhcnksIG5vdCBkaXJlY3QgcmVsYXRpb25zaGlwcykuCgpNYWtlIGZ1bmN0aW9ucyBmcm9tIHR1dG9yaWFsCmBgYHtyfQpjb252ZXJ0U0VUb1RhYmxlIDwtIGZ1bmN0aW9uKHNlX291dCxzcC5uYW1lcyl7CiAgI1RoaXMgaXMganVzdCBhIGZhbmN5IGhlbHBlciBmdW5jdGlvbiB0byBnZXQgdGhlIGRhdGEgaW4gYSBjb21wYXJhYmxlIGZvcm1hdCB0byB0aGUgb3V0cHV0IG9mIGxlc3NvbiAxIHNvIHdlIGNhbiBtYWtlIGEgc2ltaWxhciBwbG90LiBXZSB3aWxsIGNvdmVyIG90aGVyIG1ldGhvZHMgZm9yIHZpc3VhbGl6aW5nIHRoaXMgdHlwZSBvZiBvdXRwdXQgaW4gZnV0dXJlIGxlc3NvbnMuCiAgc2Vjb3IgPC0gY292MmNvcihhcy5tYXRyaXgoZ2V0T3B0Q292KHNlX291dCkpKSAjIFNlZSBzcGllY2Vhc2kgZG9jdW1lbnRhdGlvbiBmb3IgaG93IHRvIHB1bGwgb3V0IHdlaWdodHMgZm9yIGNvbXBhcmlzb24KICBlbGlzdCAgICAgPC0gc3VtbWFyeSh0cml1KHNlY29yKmdldFJlZml0KHNlX291dCksIGs9MSkpCiAgZWxpc3RbLDFdIDwtIHNwLm5hbWVzW2VsaXN0WywxXV0KICBlbGlzdFssMl0gPC0gc3AubmFtZXNbZWxpc3RbLDJdXQogIGVsaXN0Wyw0XSA8LSBwYXN0ZShlbGlzdFssMV0sZWxpc3RbLDJdKQogIGZ1bGxfZSA8LSBleHBhbmQuZ3JpZChzcC5uYW1lcyxzcC5uYW1lcykKICByb3duYW1lcyhmdWxsX2UpIDwtIHBhc3RlKGZ1bGxfZVssMV0sZnVsbF9lWywyXSkKICBmdWxsX2VbLCJXZWlnaHQiXSA8LSAwCiAgZnVsbF9lW2VsaXN0Wyw0XSwiV2VpZ2h0Il0gPC0gZWxpc3RbLDNdCiAgeCA8LSBleHBhbmQuZ3JpZCgxOmxlbmd0aChzcC5uYW1lcyksMTpsZW5ndGgoc3AubmFtZXMpKQogIGZ1bGxfZVt4WywiVmFyMSJdPnhbLCJWYXIyIl0sIldlaWdodCJdIDwtIE5BCiAgcmV0dXJuKGFzLmRhdGEuZnJhbWUoZnVsbF9lLHN0cmluZ3NBc0ZhY3RvcnM9RikpCn0KYGBgCgojIyMgU3BpZWNFYXNpIG9uIGZ1bGwgZGF0YXNldApGb2xsb3cgdGhlIHNwaWVjZWFzaSBbZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly9naXRodWIuY29tL3pkazEyMy9TcGllY0Vhc2kpIHRvIGZpbmQgb3B0aW1hbCBwYXJhbWV0ZXJzLiBBbHNvLCBiZWNhdXNlIEkgd2FudCB0byBjb21wYXJlIG5ldHdvcmtzLCB0aGlzIFtjb252b10oaHR0cHM6Ly9naXRodWIuY29tL3pkazEyMy9TcGllY0Vhc2kvaXNzdWVzLzc3KSBvbiB1c2luZyBvcHRpbWFsIHBhcmFtZXRlcnMgZm9yIGRpZmZlcmVudCBuZXR3b3JrIGNvbXBhcmlzb25zIGlzIGhlbHBmdWwuCgpSZW1vdmUgc2FtcGxlcyBmcm9tIHRoZSBwaHlsb3NlcSBvYmplY3RzIHRoYXQgYXJlIG5vdCBpbiBhbGwgMyBkb21haW5zIGFuZCByZW9yZGVyIHNhbXBsZXMgc28gdGhleSBhcmUgaW4gc2FtZSBvcmRlciBpbiBhbGwgMyBvYmplY3RzCmBgYHtyfQpiYWNfYXJjaF9jb21tb24gPC0gaW50ZXJzZWN0KHNhbXBsZV9uYW1lcyhwc19iYWNfcmFfcHJ1bmVkKSwgc2FtcGxlX25hbWVzKHBzX2FyY2hfcmFfcHJ1bmVkKSkKYWxsX2NvbW1vbiA8LSBpbnRlcnNlY3QoYmFjX2FyY2hfY29tbW9uLCBzYW1wbGVfbmFtZXMocHNfZXVrX3JhX3BydW5lZCkpCgpwc19iYWNfcHJ1bmVkIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfYmFjX3BydW5lZCkKcHNfYXJjaF9wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19hcmNoX3BydW5lZCkKcHNfZXVrX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2V1a19wcnVuZWQpCgpwc19iYWNfcmFfcHJ1bmVkIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfYmFjX3JhX3BydW5lZCkKcHNfYXJjaF9yYV9wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19hcmNoX3JhX3BydW5lZCkKcHNfZXVrX3JhX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2V1a19yYV9wcnVuZWQpCgoKb3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkKSA8LSBvdHVfdGFibGUocHNfYXJjaF9wcnVuZWQpWyxzYW1wbGVfbmFtZXMocHNfYmFjX3JhX3BydW5lZCldCm90dV90YWJsZShwc19ldWtfcHJ1bmVkKSA8LSBvdHVfdGFibGUocHNfZXVrX3BydW5lZClbLHNhbXBsZV9uYW1lcyhwc19iYWNfcmFfcHJ1bmVkKV0KCnNhbXBsZV9kYXRhKHBzX2JhY19wcnVuZWQpCnNhbXBsZV9kYXRhKHBzX2FyY2hfcHJ1bmVkKQpzYW1wbGVfZGF0YShwc19ldWtfcHJ1bmVkKQpgYGAKCgoKYGBge3J9CiNSdW4gU3BpZWNlYXNpCnBhcmdzIDwtIGxpc3Qoc2VlZD0xMDAxMCkKc2UgPC0gc3BpZWMuZWFzaShsaXN0KHBzX2JhY19wcnVuZWQsIHBzX2FyY2hfcHJ1bmVkLCBwc19ldWtfcHJ1bmVkKSwgbWV0aG9kPSdnbGFzc28nLCBsYW1iZGEubWluLnJhdGlvPTFlLTIsIG5sYW1iZGE9MTAwLCBwdWxzYXIucGFyYW1zPXBhcmdzKQpnZXRTdGFiaWxpdHkoc2UpCmBgYAoKdGhlIGFib3ZlIHRha2VzIGEgd2hpbGUgdG8gcnVuICgyMC0zMCBtaW5zKS4gVXNpbmcgcGFyYW1ldGVycyBhYm92ZSwgdGhlIHN0YWJpbGl0eSBhbG9uZyB0aGUgbGFtYmRhIHBhdGggY3Jvc3NlcyB0aGUgMC4wNSB0aHJlc2hvbGQgYW5kIHRoZSBmaW5hbCBzdGFiaWxpdHkgdmFsdWUgKDAuMDQ0KSBpcyBzdWZmaWNpZW50bHkgY2xvc2UgdG8gMC4wNQoKCgpgYGB7cn0KI1RoaXMgaXMganVzdCBhIGZhbmN5IGhlbHBlciBmdW5jdGlvbiB0byBnZXQgdGhlIGRhdGEgaW4gYSBjb21wYXJhYmxlIGZvcm1hdCB0byB0aGUgb3V0cHV0IG9mIGFib3ZlCnRhYi5zZSA8LSBjb252ZXJ0U0VUb1RhYmxlKHNlLHNwLm5hbWVzPWNvbG5hbWVzKHQoYWxsZG9tYWluc19kZl9mdWxsKSkpIAoKI1Bsb3QgCnBsb3Quc2UgPC0gZ2dwbG90KHRhYi5zZSxhZXMoeCA9IFZhcjEsIHkgPSBWYXIyLCBmaWxsID0gV2VpZ2h0KSkgKyBnZW9tX3RpbGUoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpwbG90KHBsb3Quc2UpCgpnZ3NhdmUoImZpZ3VyZXMvc3BpZWNlYXNpX2FsbGRvbWFpbnMuZXBzIixwbG90LnNlLCB3aWR0aCA9IDM1LCBoZWlnaHQgPSAzNSwgdW5pdHMgPSBjKCJpbiIpKQoKYGBgCk5vdGUtIG9ubHkgdGhlIHNpZ25pZmljYW50IHZhbHVlcyBhYm92ZSBzaG93IHVwIGluIHRoZSBoZWF0bWFwIGFib3ZlIChpZS4gdGhlcmUgaXMgbm8gInAtdmFsdWUiKQoKIyMjIFNwaWVjRWFzaSBvbiBveHljbGluZSBkZXB0aHMKCmBgYHtyfQpiYWNfYXJjaF9jb21tb24gPC0gaW50ZXJzZWN0KHNhbXBsZV9uYW1lcyhwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKSwgc2FtcGxlX25hbWVzKHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkKSkKYWxsX2NvbW1vbiA8LSBpbnRlcnNlY3QoYmFjX2FyY2hfY29tbW9uLCBzYW1wbGVfbmFtZXMocHNfZXVrX294eWNsaW5lX3BydW5lZCkpCgpwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfYmFjX294eWNsaW5lX3BydW5lZCkKcHNfYXJjaF9veHljbGluZV9wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19hcmNoX294eWNsaW5lX3BydW5lZCkKcHNfZXVrX294eWNsaW5lX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2V1a19veHljbGluZV9wcnVuZWQpCgoKb3R1X3RhYmxlKHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBvdHVfdGFibGUocHNfYXJjaF9veHljbGluZV9wcnVuZWQpWyxzYW1wbGVfbmFtZXMocHNfYmFjX294eWNsaW5lX3BydW5lZCldCm90dV90YWJsZShwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSA8LSBvdHVfdGFibGUocHNfZXVrX294eWNsaW5lX3BydW5lZClbLHNhbXBsZV9uYW1lcyhwc19iYWNfb3h5Y2xpbmVfcHJ1bmVkKV0KCnNhbXBsZV9kYXRhKHBzX2JhY19veHljbGluZV9wcnVuZWQpCnNhbXBsZV9kYXRhKHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkKQpzYW1wbGVfZGF0YShwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKQpgYGAKCgoKYGBge3J9CiNSdW4gU3BpZWNlYXNpCnBhcmdzIDwtIGxpc3Qoc2VlZD0xMDAxMCkKc2Uub3h5Y2xpbmUgPC0gc3BpZWMuZWFzaShsaXN0KHBzX2JhY19veHljbGluZV9wcnVuZWQsIHBzX2FyY2hfb3h5Y2xpbmVfcHJ1bmVkLCBwc19ldWtfb3h5Y2xpbmVfcHJ1bmVkKSwgbWV0aG9kPSdnbGFzc28nLCBsYW1iZGEubWluLnJhdGlvPTVlLTMsIG5sYW1iZGE9MzAwLCBwdWxzYXIucGFyYW1zPXBhcmdzKQpnZXRTdGFiaWxpdHkoc2Uub3h5Y2xpbmUpCmBgYAoKdGhlIGFib3ZlIHRha2VzIGEgY291cGxlIG9mIG1pbnV0ZXMgdG8gcnVuLiBTdGFiaWxpdHkgYW5kIHN0YWJpbGl0eSBhbG9uZyBsYW1iZGEgcGF0aCBhcmUgdmVyeSBzaW1pbGFyIHRvIHRoZSBmdWxsIGRhdGFzZXQgc3BpZWNlYXNpIG9iamVjdCAoc2UpIHdpdGggdGhlc2UgcGFyYW1ldGVycyBhYm92ZS4gQ29udGludWUgd2l0aCB0aGVzZS4KCgpgYGB7cn0KI1RoaXMgaXMganVzdCBhIGZhbmN5IGhlbHBlciBmdW5jdGlvbiB0byBnZXQgdGhlIGRhdGEgaW4gYSBjb21wYXJhYmxlIGZvcm1hdCB0byB0aGUgb3V0cHV0IG9mIGFib3ZlCnRhYi5zZS5veHljbGluZSA8LSBjb252ZXJ0U0VUb1RhYmxlKHNlLm94eWNsaW5lLCBzcC5uYW1lcz1jb2xuYW1lcyh0KGFsbGRvbWFpbnNfZGZfZnVsbF9veHljbGluZSkpKSAKCiNQbG90IApwbG90LnNlLm94eWNsaW5lIDwtIGdncGxvdCh0YWIuc2Uub3h5Y2xpbmUsYWVzKHggPSBWYXIxLCB5ID0gVmFyMiwgZmlsbCA9IFdlaWdodCkpICsgZ2VvbV90aWxlKCkgKyBzY2FsZV9maWxsX2dyYWRpZW50MigpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKcGxvdChwbG90LnNlLm94eWNsaW5lKQoKZ2dzYXZlKCJmaWd1cmVzL3NwaWVjZWFzaV9hbGxkb21haW5zX294eWNsaW5lLmVwcyIscGxvdC5zZS5veHljbGluZSwgd2lkdGggPSAzNSwgaGVpZ2h0ID0gMzUsIHVuaXRzID0gYygiaW4iKSkKCmBgYAoKCgojIyMgU3BpZWNFYXNpIG9uIGFub3hpYyBkZXB0aHMKCmBgYHtyfQpiYWNfYXJjaF9jb21tb24gPC0gaW50ZXJzZWN0KHNhbXBsZV9uYW1lcyhwc19iYWNfYW5veGljX3BydW5lZCksIHNhbXBsZV9uYW1lcyhwc19hcmNoX2Fub3hpY19wcnVuZWQpKQphbGxfY29tbW9uIDwtIGludGVyc2VjdChiYWNfYXJjaF9jb21tb24sIHNhbXBsZV9uYW1lcyhwc19ldWtfYW5veGljX3BydW5lZCkpCgpwc19iYWNfYW5veGljX3BydW5lZCA8LSBwcnVuZV9zYW1wbGVzKGFsbF9jb21tb24sIHBzX2JhY19hbm94aWNfcHJ1bmVkKQpwc19hcmNoX2Fub3hpY19wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19hcmNoX2Fub3hpY19wcnVuZWQpCnBzX2V1a19hbm94aWNfcHJ1bmVkIDwtIHBydW5lX3NhbXBsZXMoYWxsX2NvbW1vbiwgcHNfZXVrX2Fub3hpY19wcnVuZWQpCgoKb3R1X3RhYmxlKHBzX2FyY2hfYW5veGljX3BydW5lZCkgPC0gb3R1X3RhYmxlKHBzX2FyY2hfYW5veGljX3BydW5lZClbLHNhbXBsZV9uYW1lcyhwc19iYWNfYW5veGljX3BydW5lZCldCm90dV90YWJsZShwc19ldWtfYW5veGljX3BydW5lZCkgPC0gb3R1X3RhYmxlKHBzX2V1a19hbm94aWNfcHJ1bmVkKVssc2FtcGxlX25hbWVzKHBzX2JhY19hbm94aWNfcHJ1bmVkKV0KCnNhbXBsZV9kYXRhKHBzX2JhY19hbm94aWNfcHJ1bmVkKQpzYW1wbGVfZGF0YShwc19hcmNoX2Fub3hpY19wcnVuZWQpCnNhbXBsZV9kYXRhKHBzX2V1a19hbm94aWNfcHJ1bmVkKQpgYGAKCgoKYGBge3J9CiNSdW4gU3BpZWNlYXNpCnBhcmdzIDwtIGxpc3Qoc2VlZD0xMDAxMCkKc2UuYW5veGljIDwtIHNwaWVjLmVhc2kobGlzdChwc19iYWNfYW5veGljX3BydW5lZCwgcHNfYXJjaF9hbm94aWNfcHJ1bmVkLCBwc19ldWtfYW5veGljX3BydW5lZCksIG1ldGhvZD0nZ2xhc3NvJywgbGFtYmRhLm1pbi5yYXRpbz0xZS0xLCBubGFtYmRhPTMwMCwgcHVsc2FyLnBhcmFtcz1wYXJncykKZ2V0U3RhYmlsaXR5KHNlLmFub3hpYykKYGBgCgp0aGUgYWJvdmUgdGFrZXMgYSBjb3VwbGUgb2YgbWludXRlcyB0byBydW4KCgoKCgpgYGB7cn0KI1RoaXMgaXMganVzdCBhIGZhbmN5IGhlbHBlciBmdW5jdGlvbiB0byBnZXQgdGhlIGRhdGEgaW4gYSBjb21wYXJhYmxlIGZvcm1hdCB0byB0aGUgb3V0cHV0IG9mIGFib3ZlCnRhYi5zZS5hbm94aWMgPC0gY29udmVydFNFVG9UYWJsZShzZS5hbm94aWMsIHNwLm5hbWVzPWNvbG5hbWVzKHQoYWxsZG9tYWluc19kZl9mdWxsX2Fub3hpYykpKSAKCiNQbG90IApwbG90LnNlLmFub3hpYyA8LSBnZ3Bsb3QodGFiLnNlLmFub3hpYyxhZXMoeCA9IFZhcjEsIHkgPSBWYXIyLCBmaWxsID0gV2VpZ2h0KSkgKyBnZW9tX3RpbGUoKSArIHNjYWxlX2ZpbGxfZ3JhZGllbnQyKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKQpwbG90KHBsb3Quc2UuYW5veGljKQoKZ2dzYXZlKCJmaWd1cmVzL3NwaWVjZWFzaV9hbGxkb21haW5zX2Fub3hpYy5lcHMiLHBsb3Quc2UuYW5veGljLCB3aWR0aCA9IDM1LCBoZWlnaHQgPSAzNSwgdW5pdHMgPSBjKCJpbiIpKQoKYGBgCgoKCiMjIyBTcGllY0Vhc2kgb24gZXV4aW5pYyBkZXB0aHMKCmBgYHtyfQpiYWNfYXJjaF9jb21tb24gPC0gaW50ZXJzZWN0KHNhbXBsZV9uYW1lcyhwc19iYWNfZXV4aW5pY19wcnVuZWQpLCBzYW1wbGVfbmFtZXMocHNfYXJjaF9ldXhpbmljX3BydW5lZCkpCmFsbF9jb21tb24gPC0gaW50ZXJzZWN0KGJhY19hcmNoX2NvbW1vbiwgc2FtcGxlX25hbWVzKHBzX2V1a19ldXhpbmljX3BydW5lZCkpCgpwc19iYWNfZXV4aW5pY19wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19iYWNfZXV4aW5pY19wcnVuZWQpCnBzX2FyY2hfZXV4aW5pY19wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19hcmNoX2V1eGluaWNfcHJ1bmVkKQpwc19ldWtfZXV4aW5pY19wcnVuZWQgPC0gcHJ1bmVfc2FtcGxlcyhhbGxfY29tbW9uLCBwc19ldWtfZXV4aW5pY19wcnVuZWQpCgoKb3R1X3RhYmxlKHBzX2FyY2hfZXV4aW5pY19wcnVuZWQpIDwtIG90dV90YWJsZShwc19hcmNoX2V1eGluaWNfcHJ1bmVkKVssc2FtcGxlX25hbWVzKHBzX2JhY19ldXhpbmljX3BydW5lZCldCm90dV90YWJsZShwc19ldWtfZXV4aW5pY19wcnVuZWQpIDwtIG90dV90YWJsZShwc19ldWtfZXV4aW5pY19wcnVuZWQpWyxzYW1wbGVfbmFtZXMocHNfYmFjX2V1eGluaWNfcHJ1bmVkKV0KCnNhbXBsZV9kYXRhKHBzX2JhY19ldXhpbmljX3BydW5lZCkKc2FtcGxlX2RhdGEocHNfYXJjaF9ldXhpbmljX3BydW5lZCkKc2FtcGxlX2RhdGEocHNfZXVrX2V1eGluaWNfcHJ1bmVkKQpgYGAKCgoKYGBge3J9CiNSdW4gU3BpZWNlYXNpCnBhcmdzIDwtIGxpc3Qoc2VlZD0xMDAxMCkKc2UuZXV4aW5pYyA8LSBzcGllYy5lYXNpKGxpc3QocHNfYmFjX2V1eGluaWNfcHJ1bmVkLCBwc19hcmNoX2V1eGluaWNfcHJ1bmVkLCBwc19ldWtfZXV4aW5pY19wcnVuZWQpLCBtZXRob2Q9J2dsYXNzbycsIGxhbWJkYS5taW4ucmF0aW89MWUtNSxubGFtYmRhPTIwLCBwdWxzYXIucGFyYW1zPXBhcmdzKQpnZXRTdGFiaWxpdHkoc2UuZXV4aW5pYykKYGBgCgpJIHRyaWVkIG1hbnkgcGFyYW1ldGVycyBvbiB0aGUgYWJvdmUgYnV0IGNhbm5vdCBnZXQgYSBzYXRpc2ZhY3Rvcnkgc29sdXRpb24uIFRoZXJlIGFyZSBqdXN0IHRvbyBmZXcgc2FtcGxlcyBhZnRlciBxdWFsaXR5IGZpbHRlcmluZyB0byBkbyBTcGllY0Vhc2kgb24gdGhlIGV1eGluaWMgZGVwdGhzIG9ubHkuCgoKIyMjIFNhdmUgYW5kIHJlLWxvYWQgZW52aXJvbm1lbnQKYGBge3J9CnNhdmUuaW1hZ2UoIkVudmlyb25tZW50QmFja3Vwcy9DYXJpYWNvRXVrc19wb3N0YW5hbHlzaXNfdmFyc191cHRvX3NwaWVjZWFzaS5SRGF0YSIpCmBgYAoKT3IgbG9hZCBpZiBjb21pbmcgYmFjawpgYGB7cn0KbG9hZCgiRW52aXJvbm1lbnRCYWNrdXBzL0NhcmlhY29FdWtzX3Bvc3RhbmFseXNpc192YXJzX3VwdG9fc3BpZWNlYXNpLlJEYXRhIikKYGBgCgoKIyMgTmV0d29yayBBbmFseXNpcwpCdWlsZCB1c2luZyBpZ3JhcGgKCiMjIyBBbGwgZGVwdGhzIGFuZCBhbGwgZG9tYWlucwpgYGB7cn0KI0V4dHJhY3QgYWRqYWNlbmN5IG1hdHJpeCBmcm9tIHNwaWVjRWFzaSBvdXRwdXQKYWRqLm1hdCA8LSBnZXRSZWZpdChzZSkKdGFibGUoYXMubnVtZXJpYyhhZGoubWF0KSkKCiMgRXh0cmFjdCB3ZWlnaHRlZCBhZGphY2VuY3kKc2UuY29yICA8LSBjb3YyY29yKGFzLm1hdHJpeChnZXRPcHRDb3Yoc2UpKSkKd2VpZ2h0ZWQuYWRqLm1hdCA8LSBzZS5jb3IqZ2V0UmVmaXQoc2UpCgojQ29udmVydCB0byBncmFwaCBvYmplY3RzCmdycGgudW53ZWlnaHRlZCA8LSBhZGoyaWdyYXBoKGFkai5tYXQpCmdycGggPC0gYWRqMmlncmFwaCh3ZWlnaHRlZC5hZGoubWF0KQoKCiMgUHV0IGJhY2sgaW4gc3BlY2llcyBuYW1lcwpWKGdycGgpJG5hbWUgPC0gcm93bmFtZXMoYWxsZG9tYWluc19kZikKIyBWKGdycGgpCgojIE1ha2Ugc2l6ZSBvZiBub2RlcyBwcm9wb3J0aW9uYWwgdG8gZGVncmVlIChudW1iZXIgb2YgY29ubmVjdGlvbnMpClYoZ3JwaCkkc2l6ZSA8LSAoZGVncmVlKGdycGgpICsgMSkgIyB0aGUgKzEgYXZvaWRzIHNpemUgemVybyB2ZXJ0aWNlcwoKIyBDb2xvciBlZGdlcyBieSBjb25uZWN0aW9uIChwb3NpdGl2ZSBvciBuZWdhdGl2ZSkgCiMgRShncnBoKSRjb2xvciA8LSBjdXN0b21ibHVlZ3JlZW4KIyBFKGdycGgpJGNvbG9yW0UoZ3JwaCkkd2VpZ2h0PDBdIDwtIGN1c3RvbXJlZGRpc2hwdXJwbGUKCiMgQ2hhbmdlIHdpZHRoIG9mIGVkZ2VzIHRvIGJlIHByb3BvcnRpb25hbCB0byB0aGVpciB3ZWlnaHRzCkUoZ3JwaCkkd2lkdGggPC0gYWJzKEUoZ3JwaCkkd2VpZ2h0KSoxMAoKIyBTY2FsZSBub2RlIHNpemVzIHRvIGJlIHNtYWxsZXIKVihncnBoKSRzaXplIDwtIFYoZ3JwaCkkc2l6ZS8yCgojIFJlbW92ZSBsb3ctd2VpZ2h0IGVkZ2VzICh5b3UgZGVjaWRlIHdoYXQgdGhyZXNob2xkIGlzIHJpZ2h0IGZvciB5b3VyIG5ldHdvcmspOgojIHdlaWdodF90aHJlc2hvbGQgPC0gMC4wNwojIGdycGggPC0gZGVsZXRlLmVkZ2VzKGdycGgsd2hpY2goYWJzKEUoZ3JwaCkkd2VpZ2h0KTx3ZWlnaHRfdGhyZXNob2xkKSkKCiMgQ29sb3Igbm9kZXMgYnkgZG9tYWluCmR0eXBlIDwtIGMocmVwKCJyZWQiLG50YXhhKHBzX2JhY19wcnVuZWQpKSwgcmVwKCJncmVlbiIsbnRheGEocHNfYXJjaF9wcnVuZWQpKSwgcmVwKCJibHVlIixudGF4YShwc19ldWtfcHJ1bmVkKSkpCgojIFBsb3QKcGxvdChncnBoLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaCksCiAgICAgdmVydGV4LmNvbG9yPWR0eXBlKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBXaG9sZSBXYXRlciBDb2x1bW4iKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQmFjdGVyaWEiLCJBcmNoYWVhIiwgIkV1a2FyeWEiKSwKICAgICAgIGZpbGw9YygicmVkIiwiZ3JlZW4iLCJibHVlIiksIGJvcmRlcj1OQSkKCiMgU2F2ZSBwbG90CnNldEVQUygpCnBvc3RzY3JpcHQoZmlsZSA9ICJGaWd1cmVzLzNkb21haW5zX2FsbGRlcHRoc19zcGllY2Vhc2lfbmV0d29yay5lcHMiLCB3aWR0aCA9IDUuNSwgaGVpZ2h0ID0gNSkKcGxvdChncnBoLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaCksCiAgICAgdmVydGV4LmNvbG9yPWR0eXBlKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBXaG9sZSBXYXRlciBDb2x1bW4iKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQmFjdGVyaWEiLCJBcmNoYWVhIiwgIkV1a2FyeWEiKSwKICAgICAgIGZpbGw9YygicmVkIiwiZ3JlZW4iLCJibHVlIiksIGJvcmRlcj1OQSkKZGV2Lm9mZigpCmBgYAoKCiMjIyAzIERvbWFpbiBOZXR3b3JrLSBPeHljbGluZQpgYGB7cn0KI0V4dHJhY3QgYWRqYWNlbmN5IG1hdHJpeCBmcm9tIHNwaWVjRWFzaSBvdXRwdXQKYWRqLm1hdCA8LSBnZXRSZWZpdChzZS5veHljbGluZSkKdGFibGUoYXMubnVtZXJpYyhhZGoubWF0KSkKCiMgRXh0cmFjdCB3ZWlnaHRlZCBhZGphY2VuY3kKc2UuY29yICA8LSBjb3YyY29yKGFzLm1hdHJpeChnZXRPcHRDb3Yoc2Uub3h5Y2xpbmUpKSkKd2VpZ2h0ZWQuYWRqLm1hdCA8LSBzZS5jb3IqZ2V0UmVmaXQoc2Uub3h5Y2xpbmUpCgojQ29udmVydCB0byBncmFwaCBvYmplY3RzCmdycGgudW53ZWlnaHRlZC5veHljbGluZSA8LSBhZGoyaWdyYXBoKGFkai5tYXQpCmdycGgub3h5Y2xpbmUgPC0gYWRqMmlncmFwaCh3ZWlnaHRlZC5hZGoubWF0KQoKCiMgUHV0IGJhY2sgaW4gc3BlY2llcyBuYW1lcwpWKGdycGgub3h5Y2xpbmUpJG5hbWUgPC0gcm93bmFtZXMoYWxsZG9tYWluc19kZl9mdWxsX294eWNsaW5lKQojIFYoZ3JwaC5veHljbGluZSkKCiMgTWFrZSBzaXplIG9mIG5vZGVzIHByb3BvcnRpb25hbCB0byBkZWdyZWUgKG51bWJlciBvZiBjb25uZWN0aW9ucykKVihncnBoLm94eWNsaW5lKSRzaXplIDwtIChkZWdyZWUoZ3JwaC5veHljbGluZSkgKyAxKSAjIHRoZSArMSBhdm9pZHMgc2l6ZSB6ZXJvIHZlcnRpY2VzCgojIENvbG9yIGVkZ2VzIGJ5IGNvbm5lY3Rpb24gKHBvc2l0aXZlIG9yIG5lZ2F0aXZlKSAKIyBFKGdycGgub3h5Y2xpbmUpJGNvbG9yIDwtIGN1c3RvbWJsdWVncmVlbgojIEUoZ3JwaC5veHljbGluZSkkY29sb3JbRShncnBoLm94eWNsaW5lKSR3ZWlnaHQ8MF0gPC0gY3VzdG9tcmVkZGlzaHB1cnBsZQoKIyBDaGFuZ2Ugd2lkdGggb2YgZWRnZXMgdG8gYmUgcHJvcG9ydGlvbmFsIHRvIHRoZWlyIHdlaWdodHMKRShncnBoLm94eWNsaW5lKSR3aWR0aCA8LSBhYnMoRShncnBoLm94eWNsaW5lKSR3ZWlnaHQpKjEwCgojIFNjYWxlIG5vZGUgc2l6ZXMgdG8gYmUgc21hbGxlcgpWKGdycGgub3h5Y2xpbmUpJHNpemUgPC0gVihncnBoLm94eWNsaW5lKSRzaXplLzIKCiMgUmVtb3ZlIGxvdy13ZWlnaHQgZWRnZXMgKHlvdSBkZWNpZGUgd2hhdCB0aHJlc2hvbGQgaXMgcmlnaHQgZm9yIHlvdXIgbmV0d29yayk6CiMgd2VpZ2h0X3RocmVzaG9sZCA8LSAwLjA3CiMgZ3JwaC5veHljbGluZSA8LSBkZWxldGUuZWRnZXMoZ3JwaC5veHljbGluZSx3aGljaChhYnMoRShncnBoLm94eWNsaW5lKSR3ZWlnaHQpPHdlaWdodF90aHJlc2hvbGQpKQoKIyBDb2xvciBub2RlcyBieSBkb21haW4KZHR5cGUgPC0gYyhyZXAoInJlZCIsbnRheGEocHNfYmFjX294eWNsaW5lX3BydW5lZCkpLCByZXAoImdyZWVuIixudGF4YShwc19hcmNoX294eWNsaW5lX3BydW5lZCkpLCByZXAoImJsdWUiLG50YXhhKHBzX2V1a19veHljbGluZV9wcnVuZWQpKSkKCiMgUGxvdApwbG90KGdycGgub3h5Y2xpbmUsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLm94eWNsaW5lKSwKICAgICB2ZXJ0ZXguY29sb3I9ZHR5cGUpCnRpdGxlKCJTcGllY0Vhc2kgTmV0d29yazogQWxsIGRvbWFpbnMsIE94eWNsaW5lIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkJhY3RlcmlhIiwiQXJjaGFlYSIsICJFdWthcnlhIiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZWVuIiwiYmx1ZSIpLCBib3JkZXI9TkEpCgojIFNhdmUgcGxvdApzZXRFUFMoKQpwb3N0c2NyaXB0KGZpbGUgPSAiRmlndXJlcy8zZG9tYWluc19veHljbGluZV9zcGllY2Vhc2lfbmV0d29yay5lcHMiLCB3aWR0aCA9IDUuNSwgaGVpZ2h0ID0gNSkKcGxvdChncnBoLm94eWNsaW5lLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5veHljbGluZSksCiAgICAgdmVydGV4LmNvbG9yPWR0eXBlKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBPeHljbGluZSIpCmxlZ2VuZCgidG9wcmlnaHQiLGJ0eSA9ICJuIiwKICAgICAgIGxlZ2VuZD1jKCJCYWN0ZXJpYSIsIkFyY2hhZWEiLCAiRXVrYXJ5YSIpLAogICAgICAgZmlsbD1jKCJyZWQiLCJncmVlbiIsImJsdWUiKSwgYm9yZGVyPU5BKQpkZXYub2ZmKCkKYGBgCgojIyMgMyBEb21haW4gTmV0d29yay0gQW5veGljCmBgYHtyfQojRXh0cmFjdCBhZGphY2VuY3kgbWF0cml4IGZyb20gc3BpZWNFYXNpIG91dHB1dAphZGoubWF0IDwtIGdldFJlZml0KHNlLmFub3hpYykKdGFibGUoYXMubnVtZXJpYyhhZGoubWF0KSkKCiMgRXh0cmFjdCB3ZWlnaHRlZCBhZGphY2VuY3kKc2UuY29yICA8LSBjb3YyY29yKGFzLm1hdHJpeChnZXRPcHRDb3Yoc2UuYW5veGljKSkpCndlaWdodGVkLmFkai5tYXQgPC0gc2UuY29yKmdldFJlZml0KHNlLmFub3hpYykKCiNDb252ZXJ0IHRvIGdyYXBoIG9iamVjdHMKZ3JwaC51bndlaWdodGVkLmFub3hpYyA8LSBhZGoyaWdyYXBoKGFkai5tYXQpCmdycGguYW5veGljIDwtIGFkajJpZ3JhcGgod2VpZ2h0ZWQuYWRqLm1hdCkKCgojIFB1dCBiYWNrIGluIHNwZWNpZXMgbmFtZXMKVihncnBoLmFub3hpYykkbmFtZSA8LSByb3duYW1lcyhhbGxkb21haW5zX2RmX2Z1bGxfb3h5Y2xpbmUpCiMgVihncnBoLmFub3hpYykKCiMgTWFrZSBzaXplIG9mIG5vZGVzIHByb3BvcnRpb25hbCB0byBkZWdyZWUgKG51bWJlciBvZiBjb25uZWN0aW9ucykKVihncnBoLmFub3hpYykkc2l6ZSA8LSAoZGVncmVlKGdycGguYW5veGljKSArIDEpICMgdGhlICsxIGF2b2lkcyBzaXplIHplcm8gdmVydGljZXMKCiMgQ29sb3IgZWRnZXMgYnkgY29ubmVjdGlvbiAocG9zaXRpdmUgb3IgbmVnYXRpdmUpIAojIEUoZ3JwaC5hbm94aWMpJGNvbG9yIDwtIGN1c3RvbWJsdWVncmVlbgojIEUoZ3JwaC5hbm94aWMpJGNvbG9yW0UoZ3JwaC5hbm94aWMpJHdlaWdodDwwXSA8LSBjdXN0b21yZWRkaXNocHVycGxlCgojIENoYW5nZSB3aWR0aCBvZiBlZGdlcyB0byBiZSBwcm9wb3J0aW9uYWwgdG8gdGhlaXIgd2VpZ2h0cwpFKGdycGguYW5veGljKSR3aWR0aCA8LSBhYnMoRShncnBoLmFub3hpYykkd2VpZ2h0KSoxMAoKIyBTY2FsZSBub2RlIHNpemVzIHRvIGJlIHNtYWxsZXIKVihncnBoLmFub3hpYykkc2l6ZSA8LSBWKGdycGguYW5veGljKSRzaXplLzIKCiMgUmVtb3ZlIGxvdy13ZWlnaHQgZWRnZXMgKHlvdSBkZWNpZGUgd2hhdCB0aHJlc2hvbGQgaXMgcmlnaHQgZm9yIHlvdXIgbmV0d29yayk6CiMgd2VpZ2h0X3RocmVzaG9sZCA8LSAwLjA3CiMgZ3JwaC5hbm94aWMgPC0gZGVsZXRlLmVkZ2VzKGdycGguYW5veGljLHdoaWNoKGFicyhFKGdycGguYW5veGljKSR3ZWlnaHQpPHdlaWdodF90aHJlc2hvbGQpKQoKIyBDb2xvciBub2RlcyBieSBkb21haW4KZHR5cGUgPC0gYyhyZXAoInJlZCIsbnRheGEocHNfYmFjX2Fub3hpY19wcnVuZWQpKSwgcmVwKCJncmVlbiIsbnRheGEocHNfYXJjaF9hbm94aWNfcHJ1bmVkKSksIHJlcCgiYmx1ZSIsbnRheGEocHNfZXVrX2Fub3hpY19wcnVuZWQpKSkKCiMgUGxvdApwbG90KGdycGguYW5veGljLAogICAgIHZlcnRleC5sYWJlbD1OQSwKICAgICBsYXlvdXQ9bGF5b3V0X3dpdGhfZ3JhcGhvcHQoZ3JwaC5hbm94aWMpLAogICAgIHZlcnRleC5jb2xvcj1kdHlwZSkKdGl0bGUoIlNwaWVjRWFzaSBOZXR3b3JrOiBBbGwgZG9tYWlucywgQW5veGljIExheWVyIikKbGVnZW5kKCJ0b3ByaWdodCIsYnR5ID0gIm4iLAogICAgICAgbGVnZW5kPWMoIkJhY3RlcmlhIiwiQXJjaGFlYSIsICJFdWthcnlhIiksCiAgICAgICBmaWxsPWMoInJlZCIsImdyZWVuIiwiYmx1ZSIpLCBib3JkZXI9TkEpCgojIFNhdmUgcGxvdApzZXRFUFMoKQpwb3N0c2NyaXB0KGZpbGUgPSAiRmlndXJlcy8zZG9tYWluc19hbm94aWNfc3BpZWNlYXNpX25ldHdvcmsuZXBzIiwgd2lkdGggPSA1LjUsIGhlaWdodCA9IDUpCnBsb3QoZ3JwaC5hbm94aWMsCiAgICAgdmVydGV4LmxhYmVsPU5BLAogICAgIGxheW91dD1sYXlvdXRfd2l0aF9ncmFwaG9wdChncnBoLmFub3hpYyksCiAgICAgdmVydGV4LmNvbG9yPWR0eXBlKQp0aXRsZSgiU3BpZWNFYXNpIE5ldHdvcms6IEFsbCBkb21haW5zLCBBbm94aWMiKQpsZWdlbmQoInRvcHJpZ2h0IixidHkgPSAibiIsCiAgICAgICBsZWdlbmQ9YygiQmFjdGVyaWEiLCJBcmNoYWVhIiwgIkV1a2FyeWEiKSwKICAgICAgIGZpbGw9YygicmVkIiwiZ3JlZW4iLCJibHVlIiksIGJvcmRlcj1OQSkKZGV2Lm9mZigpCmBgYAoKIyMjIENvbGxlY3QgbmV0d29yayBwYXJhbWV0ZXJzCgojIyMjIE51bWJlciBvZiBlZGdlcwp0aGUgbnVtYmVyIG9mIGVkZ2VzIGFuZCBob3cgbWFueSBhcmUgcG9zaXRpdmUgdnMgbmVnYXRpdmUKYGBge3J9CiMgdG90YWwgbnVtYmVyIG9mIGVkZ2VzIGluIGZ1bGwgZGF0YXNldCBuZXR3b3JrCmxlbmd0aChFKGdycGgpJHdlaWdodCkKIyBwZXJjZW50IG9mIG5lZyBlZGdlcyAKKHN1bShFKGdycGgpJHdlaWdodDwwKS9sZW5ndGgoRShncnBoKSR3ZWlnaHQpKSoxMDAKCiMgdG90YWwgbnVtYmVyIG9mIGVkZ2VzIGluIG94eWNsaW5lIG5ldHdvcmsKbGVuZ3RoKEUoZ3JwaC5veHljbGluZSkkd2VpZ2h0KQojIHBlcmNlbnQgb2YgbmVnIGVkZ2VzIAooc3VtKEUoZ3JwaC5veHljbGluZSkkd2VpZ2h0PDApL2xlbmd0aChFKGdycGgub3h5Y2xpbmUpJHdlaWdodCkpKjEwMAoKIyB0b3RhbCBudW1iZXIgb2YgZWRnZXMgaW4gb3h5Y2xpbmUgbmV0d29yawpsZW5ndGgoRShncnBoLmFub3hpYykkd2VpZ2h0KQojIHBlcmNlbnQgb2YgbmVnIGVkZ2VzIAooc3VtKEUoZ3JwaC5hbm94aWMpJHdlaWdodDwwKS9sZW5ndGgoRShncnBoLmFub3hpYykkd2VpZ2h0KSkqMTAwCgpgYGAKCkRlY2xpbmluZyBudW1iZXIgb2YgdG90YWwgZWRnZXMgZ29pbmcgZnJvbSBmdWxsIGRhdGFzZXQgLS0+IG94eWNsaW5lIG9ubHkgLS0+IGFub3hpYyBvbmx5LiBCdXQgdGhlIHBlcmNlbnRhZ2Ugb2YgbmVnYXRpdmUgYXNzb2NpYXRpb25zIGlzIHNpbWlsYXIgKDM0LjQtMzcuNyUpLiBNb3N0IGFzc29jaWF0aW9ucyAofjY1JSkgaW4gZWFjaCBuZXR3b3JrIGFyZSAqcG9zaXRpdmUqLgoKIyMjIyBFZGdlIGRlbnNpdHkKdGhlIG51bWJlciBvZiBlZGdlcyByZWxhdGl2ZXMgdG8gdG90YWwgbnVtYmVyIG9mIHBvc3NpYmxlIGVkZ2VzCmBgYHtyfQplZGdlX2RlbnNpdHkoZ3JwaCkKZWRnZV9kZW5zaXR5KGdycGgub3h5Y2xpbmUpCmVkZ2VfZGVuc2l0eShncnBoLmFub3hpYykKYGBgClRoZSBmdWxsIGRhdGFzZXQgaGFzIHRoZSBoaWdoZXN0IGVkZ2UgZGVuc2l0eSwgdGhlbiBveHljbGluZSwgdGhlbiBhbm94aWMKCiMjIyMgQ29tcG9uZW50ClRoZSBzaXplIG9mIHRoZSBjb21wb25lbnRzLCBvciAiY2x1bXBzLCIgaW4gdGhlIG5ldHdvcmssIGFuZCBob3cgbWFueSBtZW1iZXJzIGluIGVhY2gKYGBge3J9CiMgZnVsbCBkYXRhc2V0CmNvbXBvbmVudHMoZ3JwaCkkbm8KY29tcG9uZW50cyhncnBoKSRjc2l6ZQoKIyBveHljbGluZQpjb21wb25lbnRzKGdycGgub3h5Y2xpbmUpJG5vCmNvbXBvbmVudHMoZ3JwaC5veHljbGluZSkkY3NpemUKCiMgYW5veGljCmNvbXBvbmVudHMoZ3JwaC5hbm94aWMpJG5vCmNvbXBvbmVudHMoZ3JwaC5hbm94aWMpJGNzaXplCmBgYAoKVGhlIGFub3hpYyBuZXR3b3JrIGlzIG1vc3QgZGlzam9pbnRlZCwgd2l0aCA0OCBjbHVtcHMgYW5kIHRoZSBsYXJnZXN0IGNvbnRhaW5pbmcgb25seSAyNCBtZW1iZXJzLiBUaGUgbmV4dCBpcyBveHljbGluZSwgd2l0aCAzMiBjbHVtcHMgYW5kIHRoZSBsYXJnZXN0IHdpdGggMTQ0IG1lbWJlcnMuIFRoZW4gdGhlIGZ1bGwgZGF0YXNldCBoYXMgb25seSAyNyBjbHVtcHMgYW5kIHRoZSBsYXJnZXN0IGNsdW1wIGNvbnRhaW5zIDI2MiBtZW1iZXJzLgoKCgojIyMjIEF2ZXJhZ2UgcGF0aCBsZW5ndGgKUGF0aCBpcyB0aGUgc2hvcnRlc3QgZGlzdGFuY2UgYmV0d2VlbiB0d28gbm9kZXMgKGZld2VzdCBudW1iZXIgb2YgZWRnZXMpLiBBdmVyYWdlIHBhdGggbGVuZ3RoIG9mIGEgbmV0d29yayBnaXZlcyBhIHNlbnNlIG9mIGhvdyBjb25uZWN0ZWQgZXZlcnkgbm9kZSBpcyB0byBhbm90aGVyLiBVbmNvbm5lY3RlZCBodWJzIGluIHRoZSBuZXRvd3JrIHdpbGwgaGF2ZSAiaW5maW5pdGUiIHBhdGhzIGZyb20gb3RoZXIgaHVicy4gVGhlIGZ1bmN0aW9uIGBtZWFuX2Rpc3RhbmNlYCBpZ25vcmVzIHRoZSBpbmZpbml0ZSBlZGdlcyBhbmQgY2FsY3VsYXRlcyB0aGUgYXZlcmFnZSBvZiBhbGwgb3RoZXIgZWRnZXMKYGBge3J9Cm1lYW5fZGlzdGFuY2UoZ3JwaCkKbWVhbl9kaXN0YW5jZShncnBoLm94eWNsaW5lKQptZWFuX2Rpc3RhbmNlKGdycGguYW5veGljKQpgYGAKVGhlIGxvbmdlc3QgYXZlcmFnZSBwYXRoIGxlbmd0aCBpcyBpbiB0aGUgb3h5Y2xpbmUsIGZvbGxvd2VkIGJ5IHRoZSB3aG9sZSBkYXRhc2V0IGFuZCB0aGVuIGFub3hpYy4gTWVhbmluZyB0aGUgbm9kZXMgaW4gdGhlIGFub3hpYyBhcmUgbW9yZSBjbG9zZWx5IGFzc29jaWF0ZWQgd2l0aCBlYWNoIG90aGVyLiBFdmVuIHRob3VnaCB0aGVyZSBhcmUgbW9yZSBodWJzIGluIGFub3hpYywgYXMgc2hvd24gYWJvdmUsIHRoZSBub2RlcyBpbiB0aGUgaHVicyBhcmUgY2xvc2UgdG8gZWFjaCBvdGhlci4gVGhlIG94eWNsaW5lIGh1YnMgaGF2ZSB0aGUgbG9uZ2VzdCBhdmVyYWdlIGRpc3RhbmNlcyBiZXR3ZWVuIG5vZGVzLgoKCgoKCgojIyMgQW5hbHl6ZSBub2RlLWxldmVsIG1lYXN1cmVzIGZyb20gZWFjaCBuZXR3b3JrCkNhbGN1bGF0ZSA0IHBhcmFtZXRlcnMgZm9yIGVhY2ggaW5kaXZpZHVhbCBub2RlOgoKLSBkZWdyZWUgKGRlKSA9IG51bWJlciBvZiBlZGdlcyBjb25uZWN0ZWQgdG8gdGhlIG5vZGUKLSBzdHJlbmd0aCAoc3QpID0gc3VtIG9mIHRoZSB3ZWlnaHRzIG9mIGFsbCB0aGUgZWRnZXMgY29ubmVjdGVkIHRvIHRoZSBub2RlCi0gYmV0d2Vlbm5lc3MgY2VudHJhbGl0eSAoYmUpID0gdGhlIG51bWJlciBvZiBzaG9ydGVzdCBwYXRocyAoYmV0d2VlbiBhbGwgb3RoZXIgbm9kZXMpIHRoYXQgZ28gdGhyb3VnaCB0aGUgbm9kZQotIGNsb3NlbmVzcyBjZW50cmFsaXR5IChjYykgPSBhdmVyYWdlIGRpc3RhbmNlIChudW1iZXIgb2YgZWRnZXMpIG9mIHRoZSBub2RlIHRvIGFueSBvdGhlciBub2RlCi0gbG9jYWwgY2x1c3RlcmluZyBjb2VmZmljaWVudCAobC5jbHVzdGVyKSAsIGFrYSB0cmFuc2l0aXZpdHkgPSB0aGUgcHJvcG9vcnRpb24gb2YgdHdvIG5vZGVzIHRoYXQgYXJlIGNvbm5lY3RlZCBieSBhIHRoaXJkIG5vZGUsIG9mIGJlaW5nIGNvbm5lY3RlZCB0byBlYWNoIG90aGVyIChlZy4gaG93IG1hbnkgb2YgYSBub2RlJ3MgImZyaWVuZHMiIGtub3cgZWFjaCBvdGhlcj8pCgpBbGwgZGVwdGggbmV0d29yay0gCmBgYHtyfQojIEZpcnN0IGNoYW5nZSB0aGUgd2VpZ2h0cyBvZiB0aGUgZWRnZXMgKHRoZSBzdHJlbmd0aCBvZiBhc3NvY2lhdGlvbikgdG8gYWJzb2x1dGUgdmFsdWUuIFRoaXMgd29uJ3Qgd29yayBpZiBuZWdhdGl2ZSBhc3NvY2lhdGlvbnMgYXJlIGxlZnQgd2l0aCBuZWdhdGl2ZSBzaWducwpFKGdycGgpJHdlaWdodCA8LSBhYnMoRShncnBoKSR3ZWlnaHQpCgpuYW1lcz1WKGdycGgpJG5hbWUKZGU9ZGVncmVlKGdycGgpCnN0PWdyYXBoLnN0cmVuZ3RoKGdycGgpCmJlPWJldHdlZW5uZXNzKGdycGgsIG5vcm1hbGl6ZWQ9VCkKY2MgPSBjbG9zZW5lc3MoZ3JwaCkKbC5jbHVzdGVyPXRyYW5zaXRpdml0eShncnBoLCAibG9jYWwiKQoKCgojIGFzc2VtYmxlIGRhdGFzZXQgYW5kIG1hdGNoIGZ1bGwgdGF4b25vbXkKZnVsbGRhdGVzZXRfbm9kZV9tZWFzdXJlcyA8LSBkYXRhLmZyYW1lKElEPW5hbWVzLCBkZWdyZWU9ZGUsIHN0cmVuZ3RoPXN0LCBiZXR3ZWVubmVzcz1iZSwgY2xvc2VuZXNzID0gY2MsIGNsdXN0ZXJpbmdfY29lZmZpY2llbnQgPSBsLmNsdXN0ZXIpIAoKIyBQdXQgYmFjayBiYWMgdGF4YW9ub215CnRlbXAxIDwtIGxlZnRfam9pbihmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzWzE6ZGltKG90dV90YWJsZShwc19iYWNfcHJ1bmVkKSlbMV0sXSwgYmFjX3RheG9ub215LCBieSA9IGMoIklEIiA9ICIjT1RVIElEIikpIAojIGRlbGV0ZSAiVGF4b25vbXktOSIgYW5kICJyZWZpbmVkIFRheG9ub215IiBjb2x1bW5zIAp0ZW1wMSA8LSBzZWxlY3QodGVtcDEsIC0idGF4b25vbXktOSIsIC0iUmVmaW5lZCB0YXhvbm9teSIpCgoKdGVtcDIgPC0gbGVmdF9qb2luKGZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXNbc3VtKGRpbShvdHVfdGFibGUocHNfYmFjX3BydW5lZCkpWzFdLDEpOnN1bShkaW0ob3R1X3RhYmxlKHBzX2JhY19wcnVuZWQpKVsxXSxkaW0ob3R1X3RhYmxlKHBzX2FyY2hfcHJ1bmVkKSlbMV0pLF0sIGFyY2hfdGF4b25vbXksIGJ5ID0gYygiSUQiID0gIiNPVFUgSUQiKSkgCgoKdGVtcDMgPC0gbGVmdF9qb2luKGZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXNbc3VtKGRpbShvdHVfdGFibGUocHNfYXJjaF9wcnVuZWQpKVsxXSwgZGltKG90dV90YWJsZShwc19iYWNfcHJ1bmVkKSlbMV0sMSk6c3VtKGRpbShvdHVfdGFibGUocHNfYXJjaF9wcnVuZWQpKVsxXSwgZGltKG90dV90YWJsZShwc19iYWNfcHJ1bmVkKSlbMV0sZGltKG90dV90YWJsZShwc19ldWtfcHJ1bmVkKSlbMV0pLF0sIGV1a190YXhvbm9teSwgYnkgPSBjKCJJRCIgPSAiI0FTViBJRCIpKSAKIyBSZW5hbWUgY29sIG5hbWVzIHRvIG1hdGNoIHRob3NlIGZyb20gQmFjIGFuZCBBcmNoCnRlbXAzIDwtIHRlbXAzICU+JQogIHJlbmFtZSgidGF4b25vbXktMSIgPSBLaW5nZG9tLCAidGF4b25vbXktMiIgPSBTdXBlcmdyb3VwLCAidGF4b25vbXktMyIgPSBEaXZpc2lvbiwgInRheG9ub215LTQiID0gQ2xhc3MsICJ0YXhvbm9teS01IiA9IE9yZGVyLCAidGF4b25vbXktNiIgPSBGYW1pbHksICJ0YXhvbm9teS03IiA9IEdlbnVzLCAidGF4b25vbXktOCIgPSBTcGVjaWVzKQoKIyBjb21iaW5lIGJhY2sgYWxsIDMgZG9tYWlucywgd2l0aCBuZXcgbmFtZXMgYXMgcm93IG5hbWVzIGluIGEgZGF0YWZyYW1lCmZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMgPC0gcmJpbmQodGVtcDEsIHRlbXAyLCB0ZW1wMykKZnVsbGRhdGVzZXRfbm9kZV9tZWFzdXJlcwpgYGAKClBsb3QgYmV0d2VlbmVzcyB2cyBkZWdyZWUgZm9yIGVhY2ggbm9kZS4gCi0gVGlwdG9uIGV0IGFsLiBhcmd1ZSB0aGF0IG5vZGVzIHdpdGggaGlnaCBiZXR3ZWVubmVzcyBhcmUgImJvdHRsZW5lY2tzIiBvciBpbXBvcnRhbnQgY29ubmVjdG9ycyBhbmQgbm9kZXMgd2l0aCBoaWdoIGRlZ3JlZSBhcmUgImh1YnMiCi0gQmVycnkgZXQgYWwuIGFyZ3VlIHRoYXQgbm9kZXMgd2l0aCBsb3cgYmV0d2Vlbm5lc3MsIGhpZ2ggZGVncmVlLCBoaWdoIGNsb3NlbmVzcywgYW5kIGhpZ2ggdHJhbnNpdGl2aXR5IGFyZSBjYW5kaWRhdGUga2V5c3RvbmUgc3BlY2llcwogIC0gQWRkIGluIGNsb3NlbmVzcyBpbnRvIHRoZSBub2RlJ3MgcGxvdGx5IGxhYmVsIHNpbmNlIHRoZXNlIGRvbid0IHZhcnkgbXVjaCBub2RlLXRvLW5vZGUgYW5kIHdvdWxkbid0IG1ha2Ugc2Vuc2UgdG8gcGxvdApgYGB7cn0KIyByZXBsYWNlIE5BIGluIHRheG9ub215IHdpdGggdW5pZGVudGlmaWVkIAojIHJlbW92ZSBub2RlcyB3aXRoIDAgYmV0d2Vlbm5lc3MgKGNhbid0IGNhbGN1bGF0ZSBsb2cxMCBvZiAwKQojIHJlcGxhY2UgTmFOIGNsdXN0ZXJpbmcgY29lZnMgd2l0aCAwCmZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMgPC0gZnVsbGRhdGVzZXRfbm9kZV9tZWFzdXJlcyAlPiUgCiAgcmVwbGFjZShpcy5uYSguKSwgInVuaWRlbnRpZmllZCIpICU+JQogIGZpbHRlcighYmV0d2Vlbm5lc3MgPT0gMCkgCgojIGdldCBlbm91Z2ggY29sb3JzIGFuZCByYW5kb21seSByZWFycmFuZ2Ugc28gdGhleSBhcmUgZWFzaWVyIHRvIHNlcGFyYXRlIG9uIHRoZSBwbG90Cm15Y29sb3JzIDwtIGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCgxMiwgIlBhaXJlZCIpKShsZW5ndGgodW5pcXVlKGZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMkYHRheG9ub215LTNgKSkpCm15Y29sb3JzIDwtICBzYW1wbGUobXljb2xvcnMpCgojIHBsb3Qgd2l0aCBwbG90bHkgYW5kIHNvIEkgY2FuIGhvdmVyIG92ZXIgcG9pbnRzIGFuZCBkZXRlcm1pbmUgd2hpY2ggdGF4YSB0aGV5IGFyZQpwIDwtIGdncGxvdChmdWxsZGF0ZXNldF9ub2RlX21lYXN1cmVzLCBhZXMoeCA9IGRlZ3JlZSwgeSA9IGJldHdlZW5uZXNzLCBJRCA9IElELCBzaGFwZSA9IGB0YXhvbm9teS0xYCwgYHRheG9ub215LTJgID0gYHRheG9ub215LTJgLCBjb2xvciA9IGB0YXhvbm9teS0zYCwgYHRheG9ub215LTRgID0gYHRheG9ub215LTRgLCBgdGF4b25vbXktNWAgPSBgdGF4b25vbXktNWApKSArCiAgZ2VvbV9wb2ludChzaXplID0gNSkgKwogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0nbG9nMTAnKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IG15Y29sb3JzKSArCiAgdGhlbWVfYncoKQpnZ3Bsb3RseShwLCB0b29sdGlwID0gYygiSUQiLCJ0YXhvbm9teS0yIiwgInRheG9ub215LTMiLCAidGF4b25vbXktNCIsICJ0YXhvbm9teS01IikpCgpgYGAKClBsb3Qgbm9kZSBkZWdyZWUgYW5kIHRyYW5zaXRpdml0eSAoY2x1c3RlcmluZyBjb2VmZmljaWVudCkKYGBge3J9CiMgcGxvdCB3aXRoIHBsb3RseSBhbmQgc28gSSBjYW4gaG92ZXIgb3ZlciBwb2ludHMgYW5kIGRldGVybWluZSB3aGljaCB0YXhhIHRoZXkgYXJlCnAgPC0gZ2dwbG90KGZ1bGxkYXRlc2V0X25vZGVfbWVhc3VyZXMsIGFlcyh4ID0gY2x1c3RlcmluZ19jb2VmZmljaWVudCwgeSA9IGJldHdlZW5uZXNzLCBJRCA9IElELCBzaGFwZSA9IGB0YXhvbm9teS0xYCwgYHRheG9ub215LTJgID0gYHRheG9ub215LTJgLCBjb2xvciA9IGB0YXhvbm9teS0zYCwgYHRheG9ub215LTRgID0gYHRheG9ub215LTRgLCBgdGF4b25vbXktNWAgPSBgdGF4b25vbXktNWApKSArCiAgZ2VvbV9wb2ludChzaXplID0gNSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBteWNvbG9ycykgKwogICAgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zPSdsb2cxMCcpICsKICB0aGVtZV9idygpCmdncGxvdGx5KHAsIHRvb2x0aXAgPSBjKCJJRCIsInRheG9ub215LTIiLCAidGF4b25vbXktMyIsICJ0YXhvbm9teS00IiwgInRheG9ub215LTUiKSkKCmBgYAoKCgoKIyMgU1RPUFBFRCBIRVJFLiBDT05USU5VRSBQTEFZSU5HIFdJVEggUExPVCBBQk9WRSwgaGlnaGxpdGluZyBzcGVjaWZpYyBncm91cHMgKFN5bmQsIFNwdW1lbGxhcmlkZSwgQWxwaGFzLCBEZWZlcnJpYmFjLCBldGMpLiBETyBTQU1FIEZPUiBPWFlDTElORSBBTkQgQU5PWElDIE9OTFkgKFdJTEwgTkVFRCBUTyBNQUtFIEVESVRTIElOIEZJTFRFUklORyBBQk9WRSBGT1IgQ0xFQVIgTkFNRVMpLiBDT05USU5VRSBUTyBDT0xMRUNUIE9WRVJBTEwgTkVUV09SSyBQQVJBTUVURVJTIEZPTExPV0lORzogaHR0cHM6Ly9kc2hpenVrYS5naXRodWIuaW8vbmV0d29ya2FuYWx5c2lzLzA0X21lYXN1cmluZy5odG1sI2JldHdlZW5uZXNzLiBSRU1PVkUgRVVLUyBUTyBTRUUgSE9XIE5FVE9XT1JLUyBDSEFOR0UKLSByZXBvcnQgYmxhc3QvIG5ldyBTSUxWQSByZXN1bHRzIGZvciAKICAtICJObyBibGFzdCBoaXQiIGRlbm92bzEyOTc5NwogIC0gIkFFR0FOLTI0NSIgZnJvbSBhIG1ldGFnZW5vbWUgZGVub3ZvMzUxOTEwCiAgCiAgRmlndXJlIG91dCBob3cgdG8gY2FsY3VsYXRlIGNsb3NlbmVzcyBmb3Igb25seSBtYWpvciBodWIsIHJhdGhlciB0aGFuIHdob2xlIAoKCgoKCg==